[JSOI2008]火星人 题解
题目地址:ė …
May all the beauty be blessed.
题目地址:BZOJ:Problem 3938. — Robot
小q有n只机器人,一开始他把机器人放在了一条数轴上,第i只机器人在ai的位置上静止,而自己站在原点。在这之后小q会执行一些操作,他想要命令一个机器人向左或者向右移动x格。但是机器人似乎听不清小q的命令,事实上它们会以每秒x格的速度匀速移动。看着自己的机器人越走越远,小q很着急,他想知道当前离他(原点)最远的机器人有多远。具体的操作以及询问见输入格式。注意,不同的机器人之间互不影响,即不用考虑两个机器人撞在了一起的情况。
输入格式:
共有m个事件,输入将会按事件的时间顺序给出。第一行两个正整数n,m。接下来一行n个整数,第i个数是ai,表示第i个机器人初始的位置(初始移动速度为0)。接下来m行,每行行首是一个非负整数ti,表示该事件点发生的时刻(以秒为单位)。第二个是一个字符串S,代表操作的种类。数字与字符串之间用一个空格隔开。接下来的输入按S的种类分类。若S是command
(不带引号),则接下来两个整数ki,xi,表示小q对第ki个机器人执行了操作,该机器人的速度将会被重置,变为向数轴正方向每秒移动xi格(若xi为负数就相当于向数轴负方向每秒移动∣xi∣格)。保证1≤ki≤n。若S是query
(不带引号),则你需要输出当前离原点最远的机器人有多远。保证t1≤t2≤t2≤…≤tm。(注:若同一时间发生多次操作,则按读入顺序依次执行)
输出格式:
对于每个query
询问,输出一行,包含一个整数表示正确的答案。C/C++输入输出long long时请用%lld。由于本题数据量较大,建议不要使用cin/cout进行输入输出。
输入样例#1:
4 5 -20 0 20 100 10 command 1 10 20 command 3 -10 30 query 40 command 1 -30 50 query
输出样例#1:
180 280
样例说明:
第一个命令执行时,各个机器人的位置为:−20,0,20,100。
第二个命令执行时,各个机器人的位置为:80,0,20,100。
第一个询问时,各个机器人的位置为:180,0,−80,100。
第三个命令执行时,各个机器人的位置为:280,0,−180,100。
第二个询问时,各个机器人的位置为:−20,0,−280,100。
数据范围:
设 command
的个数为 C,query 的个数为 Q。(所以 C+Q=m)
对于所有的事件满足0 \leq t_i \leq 10^9,对于所有的 command
满足 |x_i| \leq 10^4。
对于所有的机器人满足|a_i| \leq 10^9。
N, C \leq 10^5
Q \leq 5 \times 10^5
首先考虑机器人的位移-时间图像一定是折线图,我们把折线拆成线段,加入标记永久化线段树里。考虑离线处理需要加入的线段,并且离散化时间。
关于标记永久化线段树存入线段的用法,这个题是一个入门题:[JSOI2008]Blue Mary开公司 题解 | KSkun’s Blog,本题也需要像这个题一样画图来观察直线间的关系,只不过那个题斜率一定为正,这个题斜率可以为负。
对于每一个询问,需要找到该询问处的最大值和最小值,因为位移可为负数,取绝对值后也可以很大。正因如此,我们需要两棵线段树,一棵存最大值,一棵存最小值。
// Code by KSkun, 2018/2
#include <cstdio>
#include <vector>
#include <algorithm>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}
inline LL readint() {
register LL res = 0, neg = 1;
char c = fgc();
while (c < '0' || c > '9') {
if(c == '-') neg = -1;
c = fgc();
}
while (c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = fgc();
}
return res * neg;
}
const int MAXQ = 600005, MAXN = 100005;
inline bool isop(char c) {
return c == 'c' || c == 'q';
}
inline char readop() {
char c = fgc();
while (!isop(c)) c = fgc();
return c;
}
// global variables
struct Line {
LL k, b;
Line() {}
Line(LL k, LL b): k(k), b(b) {}
inline LL cal(LL x) {
return k * x + b;
}
};
Line lstl[MAXN];
LL n, m, N, lstt[MAXN];
struct Query {
LL time, x, k;
char op;
} ques[MAXQ];
inline bool cmp(Line l1, Line l2, LL x) { // if l1 bigger than l2 on pos x
return l1.cal(x) > l2.cal(x);
}
inline double interx(Line l1, Line l2) {
return double(l2.b - l1.b) / (l1.k - l2.k);
}
std::vector<LL> tmp;
// seg tree
#define lch o << 1
#define rch o << 1 | 1
#define mid ((l + r) >> 1)
Line tmx[MAXQ << 2], tmn[MAXQ << 2];
bool imx[MAXQ << 2], imn[MAXQ << 2];
inline void addmx(int o, int l, int r, int ll, int rr, Line line) {
if(l >= ll && r <= rr) {
if(!imx[o]) {
tmx[o] = line;
imx[o] = true;
return;
}
LL olv = tmx[o].cal(tmp[l]), orv = tmx[o].cal(tmp[r]), nlv = line.cal(tmp[l]), nrv = line.cal(tmp[r]);
if(olv >= nlv && orv >= nrv) {
return;
}
if(olv <= nlv && orv <= nrv) {
tmx[o] = line;
return;
}
double ix = interx(tmx[o], line);
if(olv >= nlv) {
if(ix <= tmp[mid]) {
addmx(lch, l, mid, ll, rr, tmx[o]);
tmx[o] = line;
} else {
addmx(rch, mid + 1, r, ll, rr, line);
}
} else {
if(ix > tmp[mid]) {
addmx(rch, mid + 1, r, ll, rr, tmx[o]);
tmx[o] = line;
} else {
addmx(lch, l, mid, ll, rr, line);
}
}
return;
}
if(ll <= mid) addmx(lch, l, mid, ll, rr, line);
if(rr > mid) addmx(rch, mid + 1, r, ll, rr, line);
}
inline void addmn(int o, int l, int r, int ll, int rr, Line line) {
if(l >= ll && r <= rr) {
if(!imn[o]) {
tmn[o] = line;
imn[o] = true;
return;
}
LL olv = tmn[o].cal(tmp[l]), orv = tmn[o].cal(tmp[r]), nlv = line.cal(tmp[l]), nrv = line.cal(tmp[r]);
if(olv <= nlv && orv <= nrv) {
return;
}
if(olv >= nlv && orv >= nrv) {
tmn[o] = line;
return;
}
double ix = interx(tmn[o], line);
if(olv < nlv) {
if(ix <= tmp[mid]) {
addmn(lch, l, mid, ll, rr, tmn[o]);
tmn[o] = line;
} else {
addmn(rch, mid + 1, r, ll, rr, line);
}
} else {
if(ix > tmp[mid]) {
addmn(rch, mid + 1, r, ll, rr, tmn[o]);
tmn[o] = line;
} else {
addmn(lch, l, mid, ll, rr, line);
}
}
return;
}
if(ll <= mid) addmn(lch, l, mid, ll, rr, line);
if(rr > mid) addmn(rch, mid + 1, r, ll, rr, line);
}
inline LL quemx(int o, int l, int r, int x) {
LL res = -1e15;
if(imx[o]) res = std::max(res, tmx[o].cal(tmp[x]));
if(l == r) return res;
if(x <= mid) res = std::max(res, quemx(lch, l, mid, x));
if(x > mid) res = std::max(res, quemx(rch, mid + 1, r, x));
return res;
}
inline LL quemn(int o, int l, int r, int x) {
LL res = 1e15;
if(imn[o]) res = std::min(res, tmn[o].cal(tmp[x]));
if(l == r) return res;
if(x <= mid) res = std::min(res, quemn(lch, l, mid, x));
if(x > mid) res = std::min(res, quemn(rch, mid + 1, r, x));
return res;
}
// main
int main() {
n = readint();
m = readint();
for(int i = 1; i <= n; i++) {
lstl[i].b = readint();
}
for(int i = 1; i <= m; i++) {
ques[i].time = readint();
ques[i].op = readop();
if(ques[i].op == 'c') {
ques[i].x = readint();
ques[i].k = readint();
}
tmp.push_back(ques[i].time);
}
tmp.push_back(-1);
tmp.push_back(0);
std::sort(tmp.begin(), tmp.end());
tmp.erase(std::unique(tmp.begin(), tmp.end()), tmp.end());
N = tmp.size() - 1;
for(int i = 1; i <= m; i++) {
if(ques[i].op == 'c') {
LL x = ques[i].x,
lst = std::lower_bound(tmp.begin(), tmp.end(), lstt[x]) - tmp.begin(),
now = std::lower_bound(tmp.begin(), tmp.end(), ques[i].time) - tmp.begin();
addmx(1, 1, N, lst, now, lstl[x]);
addmn(1, 1, N, lst, now, lstl[x]);
lstl[x] = Line(ques[i].k, lstl[x].b + ques[i].time * (lstl[x].k - ques[i].k));
lstt[x] = ques[i].time;
}
}
for(int i = 1; i <= n; i++) {
LL lst = std::lower_bound(tmp.begin(), tmp.end(), lstt[i]) - tmp.begin();
addmx(1, 1, N, lst, N, lstl[i]);
addmn(1, 1, N, lst, N, lstl[i]);
}
for(int i = 1; i <= m; i++) {
if(ques[i].op == 'q') {
LL now = std::lower_bound(tmp.begin(), tmp.end(), ques[i].time) - tmp.begin(),
ans1 = quemx(1, 1, N, now), ans2 = -quemn(1, 1, N, now);
printf("%lld\n", std::max(ans1, ans2));
}
}
return 0;
}
题目地址:洛谷:【P4246】[SHOI2008]堵塞的交通 – 洛谷、BZOJ:Problem 1018. — [SHOI2008]堵塞的交通traffic
有一天,由于某种穿越现象作用,你来到了传说中的小人国。小人国的布局非常奇特,整个国家的交通系统可以被看成是一个2行C列的矩形网格,网格上的每个点代表一个城市,相邻的城市之间有一条道路,所以总共有2C个城市和3C-2条道路。
小人国的交通状况非常槽糕。有的时候由于交通堵塞,两座城市之间的道路会变得不连通,直到拥堵解决,道路才会恢复畅通。初来咋到的你决心毛遂自荐到交通部某份差事,部长听说你来自一个科技高度发达的世界,喜出望外地要求你编写一个查询应答系统,以挽救已经病入膏肓的小人国交通系统。 小人国的交通部将提供一些交通信息给你,你的任务是根据当前的交通情况回答查询的问题。交通信息可以分为以下几种格式:
Close r1 c1 r2 c2
:相邻的两座城市(r1, c1)和(r2, c2)之间的道路被堵塞了;Open r1 c1 r2 c2
:相邻的两座城市(r1, c1)和(r2, c2)之间的道路被疏通了;Ask r1 c1 r2 c2
:询问城市(r1, c1)和(r2, c2)是否连通。如果存在一条路径使得这两条城市连通,则返回Y
,否则返回N
。注:ri表示行数,ci表示列数,1 \leq r_i \leq 2, 1 \leq c_i \leq C。
输入格式:
第一行只有一个整数C,表示网格的列数。接下来若干行,每行为一条交通信息,以单独的一行Exit
作为结束。我们假设在一开始所有的道路都是堵塞的。我们保证C小于等于100000,信息条数小于等于100000。
输出格式:
对于每个查询,输出一个Y
或N
。
输入样例#1:
2 Open 1 1 1 2 Open 1 2 2 2 Ask 1 1 2 2 Ask 2 1 2 2 Exit
输出样例#1:
Y N
数据范围:
对于100%的数据,1 \leq C \leq 100000,1 \leq信息条数\leq 100000。
本题是一个线段树维护连通性的题目,那么必然要设计区间信息的合并,设计一个可合并的状态很有必要。这里我的设计是对于区间[l, r]存储(1, l)、(2, l)、(1, r)、(2, r)四个点两两间的连通性,总共有6种(即左上左下右上右下)。另外把横向道路放在线段树外面管理。
合并的时候,我们分情况讨论。例如需要维护左下右上这一情况,可以走的路线一共两种,如下图。
将六种情况都处理完毕合并就完成了。
竖向道路:找到包含这条道路的长度为1的区间(即[x, x]),修改该区间内四种状态(左上右上、左下右下永远连通)即可。
横向道路:线段树递归的时候,发现有一个区间的mid和mid+1恰好是横向道路两端的c坐标时,在外部维护的横向道路修改后更新这个区间的信息,因为横向道路的影响是从这个区间开始产生的。
直接查[c1, c2]?显然不行,因为有下面这种情况:
不一定直接能到,也许要往左往右绕,所以我们还得考虑绕的情况。除了[c1, c2]以外,还要把[1, c1]、[c2, C]两个区间的信息拿到,查c1上下两点,c2上下两点,再查这4个点之间的连通情况。
线段树维护连通性,很新奇的用法。这道题的思维难度并不大,但是实现难度不小。主要细节集中于合并区间信息与查询答案的时候。写这个题的时候,用别人题解里的程序拍了好久才改出来。总之是很烦人的就对了。
注:区间信息中,lurd
是左(left)上(up)右(right)上(down)的意思,其他的以此类推。ru
数组是上方(up)的横向道路(road),rd
以此类推。
// Code by KSkun, 2018/2
#include <cstdio>
#include <cstring>
#include <algorithm>
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF
: *p1++;
}
inline int readint() {
register int res = 0, neg = 1;
char c = fgc();
while (c < '0' || c > '9') {
if(c == '-') neg = -1;
c = fgc();
}
while (c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = fgc();
}
return res * neg;
}
const int MAXN = 100005;
int c, r1, c1, r2, c2;
char op;
inline bool isop(char c) {
return c == 'O' || c == 'C' || c == 'A' || c == 'E';
}
inline char readop() {
char c = fgc();
while (!isop(c)) c = fgc();
return c;
}
#define lch o << 1
#define rch o << 1 | 1
#define mid ((l + r) >> 1)
bool ru[MAXN], rd[MAXN];
struct Data {
int l, r;
bool luru, ldrd, luld, rurd, lurd, ldru;
} tree[MAXN << 2];
inline void merge(Data *rt, Data ls, Data rs) { // 合并区间信息
rt->l = ls.l;
rt->r = rs.r;
rt->luru = (ls.luru && ru[ls.r] && rs.luru) || (ls.lurd && rd[ls.r] && rs.ldru); // 左上右上
rt->lurd = (ls.luru && ru[ls.r] && rs.lurd) || (ls.lurd && rd[ls.r] && rs.ldrd); // 左上右下
rt->ldru = (ls.ldru && ru[ls.r] && rs.luru) || (ls.ldrd && rd[ls.r] && rs.ldru); // 左下右上
rt->ldrd = (ls.ldru && ru[ls.r] && rs.lurd) || (ls.ldrd && rd[ls.r] && rs.ldrd); // 左下右下
rt->luld = ls.luld || (ls.luru && ru[ls.r] && rs.luld && rd[ls.r] && ls.ldrd); // 左上左下
rt->rurd = rs.rurd || (rs.luru && ru[ls.r] && ls.rurd && rd[ls.r] && rs.ldrd); // 右上右下
}
inline void build(int o, int l, int r) {
if (l == r) {
tree[o].l = tree[o].r = l;
tree[o].luru = tree[o].ldrd = true;
return;
}
build(lch, l, mid);
build(rch, mid + 1, r);
merge(&tree[o], tree[lch], tree[rch]);
}
inline void modify1(int o, int l, int r, int x, bool comm) {
if (l == r) {
tree[o].luld = tree[o].rurd = tree[o].lurd = tree[o].ldru = comm;
return;
}
if (x <= mid) modify1(lch, l, mid, x, comm);
if (x > mid) modify1(rch, mid + 1, r, x, comm);
merge(&tree[o], tree[lch], tree[rch]);
}
inline void modify2(int o, int l, int r, int x, bool up, bool comm) {
if (mid == x) {
if(up) ru[x] = comm; else rd[x] = comm;
merge(&tree[o], tree[lch], tree[rch]);
return;
}
if (x <= mid) modify2(lch, l, mid, x, up, comm);
if (x > mid) modify2(rch, mid + 1, r, x, up, comm);
merge(&tree[o], tree[lch], tree[rch]);
}
inline Data query(int o, int l, int r, int ll, int rr) {
if (l >= ll && r <= rr) {
return tree[o];
}
if (rr <= mid) return query(lch, l, mid, ll, rr);
else if (ll > mid) return query(rch, mid + 1, r, ll, rr);
else {
Data res;
merge(&res, query(lch, l, mid, ll, mid), query(rch, mid + 1, r, mid + 1, rr));
return res;
}
}
int main() {
c = readint();
build(1, 1, c);
for (;;) {
op = readop();
if (op == 'E') break;
r1 = readint();
c1 = readint();
r2 = readint();
c2 = readint();
if (c1 > c2) {
std::swap(r1, r2);
std::swap(c1, c2);
}
if (op == 'O') {
if (r1 == r2) {
modify2(1, 1, c, c1, r1 == 1, true);
} else {
modify1(1, 1, c, c1, true);
}
}
if (op == 'C') {
if (r1 == r2) {
modify2(1, 1, c, c1, r1 == 1, false);
} else {
modify1(1, 1, c, c1, false);
}
}
if (op == 'A') {
Data rm = query(1, 1, c, c1, c2), rl = query(1, 1, c, 1, c1),
rr = query(1, 1, c, c2, c);
bool b1, b2, b3, b4;
if (r1 == 1 && r2 == 1) {
b1 = rm.luru;
b2 = rl.rurd && rm.ldru;
b3 = rr.luld && rm.lurd;
b4 = rl.rurd && rm.ldrd && rr.luld;
if(b1 || b2 || b3 || b4) printf("Y\n"); else printf("N\n");
} else if (r1 == 1 && r2 == 2) {
b1 = rm.lurd;
b2 = rl.rurd && rm.ldrd;
b3 = rr.luld && rm.luru;
b4 = rl.rurd && rm.ldru && rr.luld;
if(b1 || b2 || b3 || b4) printf("Y\n"); else printf("N\n");
} else if (r1 == 2 && r2 == 1) {
b1 = rm.ldru;
b2 = rl.rurd && rm.luru;
b3 = rr.luld && rm.ldrd;
b4 = rl.rurd && rm.lurd && rr.luld;
if(b1 || b2 || b3 || b4) printf("Y\n"); else printf("N\n");
} else {
b1 = rm.ldrd;
b2 = rl.rurd && rm.lurd;
b3 = rr.luld && rm.ldru;
b4 = rl.rurd && rm.luru && rr.luld;
if(b1 || b2 || b3 || b4) printf("Y\n"); else printf("N\n");
}
}
}
return 0;
}