@(BZOJ)[线段树]
Description
有一天,由于某种穿越现象作用,你来到了传说中的小人国。小人国的布局非常奇特,整个国家的交通系统可
以被看成是一个(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;
Input
第一行只有一个整数(C),表示网格的列数。接下来若干行,每行为一条交通信息,以单独的一行“Exit”作为
结束。我们假设在一开始所有的道路都是堵塞的。我们保证 C小于等于100000,信息条数小于等于(100000)。
Output
对于每个查询,输出一个“Y”或“N”。
Sample Input
Open 1 1 1 2
Open 1 2 2 2
Ask 1 1 2 2
Ask 2 1 2 2
Exit
Sample Output
Y
N
Solution
這一題還是很難想的, 用的是線段樹維護連通性.
经过分析, ((r1,c1))( o)((r2,c2))一共有四种方式。
- 直接过去。
- 先到((0,c1))再过去。
- 先到((1,c2))再过去。
- 先到((0,c1))再到((1,c2))再过去。
用一个数组(a1[i][j]),表示x节点维护的区间最左端的第i行能否从区间内部通行到最右端的第j行;
另一个数组(a2[i]),(a2[0])记录x节点维护的区间中最坐端的点能不能从区间内部绕到另外一行, (a2[1])记录最右端的点能不能从区间内部绕到另外一行.
大概就是这样吧, 然后合并两个节点的操作会比较奇怪, 可以直接看代码.
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;
inline void read(int &x)
{
x = 0;
int flag = 1;
char c;
while(! isdigit(c = getchar()))
if(c == '-')
flag *= - 1;
while(isdigit(c))
x = x * 10 + c - '0', c = getchar();
x *= flag;
}
inline void read(char *s)
{
char c;
while(! isgraph(c = getchar()));
int len = 0;
while(isgraph(c))
s[len ++] = c, c = getchar();
}
void println(int x)
{
if(x < 0)
putchar('-'), x *= - 1;
if(x == 0)
putchar('0');
int ans[10 + (1 << 4)], top = 0;
while(x)
ans[top ++] = x % 10, x /= 10;
for(; top; top --)
putchar(ans[top - 1] + '0');
putchar('
');
}
const int C = 1 << 17;
int c;
struct Node
{
int a1[2][2], a2[2];
}T[C << 2];
int b[C << 2][2];
void Build(int u, int L, int R)
{
for(int i = 0; i < 2; i ++)
for(int j = 0; j < 2; j ++)
T[u].a1[i][j] = T[u].a2[i] = 0;
if(L == R)
T[u].a1[0][0] = T[u].a1[1][1] = 1;
int mid = L + R >> 1;
if(L < R)
Build(u << 1, L, mid), Build(u << 1 ^ 1, mid + 1, R);
}
Node Update(Node x1, Node x2, int b[])
{
Node ret;
for(int i = 0; i < 2; i ++)
for(int j = 0; j < 2; j ++)
ret.a1[i][j] = x1.a1[i][0] && b[0] && x2.a1[0][j] || x1.a1[i][1] && b[1] && x2.a1[1][j];
ret.a2[0]=x1.a2[0] || x1.a1[0][0] && b[0] && x2.a2[0] && b[1] && x1.a1[1][1];
ret.a2[1]=x2.a2[1] || x2.a1[0][0] && b[0] && x1.a2[1] && b[1] && x2.a1[1][1];
return ret;
}
void Change(int k, int u, int L, int R, int r1, int c1, int r2, int c2)
{
if(r1 == r2 && c1 == L + R >> 1)
b[u][r1] = k, T[u] = Update(T[u << 1], T[u << 1 | 1], b[u]);
else if(L == R)
T[u].a1[0][1] = T[u].a1[1][0] = T[u].a2[0] = T[u].a2[1] = k;
else
{
int mid = L + R >> 1;
if(c2 <= mid)
Change(k, u << 1, L, mid, r1, c1, r2, c2);
else
Change(k, u << 1 | 1, mid + 1, R, r1, c1, r2, c2);
T[u] = Update(T[u << 1], T[u << 1 | 1], b[u]);
}
}
Node Access(int u, int L, int R, int c1, int c2)
{
int mid = L + R >> 1;
if(L >= c1 && R <= c2)
return T[u];
if(c2 <= mid)
return Access(u << 1, L, mid, c1, c2);
else if (c1 > mid)
return Access(u << 1 | 1, mid + 1, R, c1, c2);
else
return Update(Access(u << 1, L, mid, c1, c2), Access(u << 1 | 1, mid + 1, R, c1, c2), b[u]);
}
void Ask(int r1, int c1, int r2, int c2)
{
Node Left = Access(1, 1, c, 1, c1), Right = Access(1, 1, c, c2, c), Mid = Access(1, 1, c, c1, c2);
int ret = 0;
for(int i = 0; i < 2; i ++)
for(int j = 0; j < 2; j ++)
if(Mid.a1[i][j] && (i == r1 || Left.a2[1]) && (j == r2 || Right.a2[0]))
{
ret = 1;
break;
}
putchar(ret ? 'Y' : 'N');
putchar('
');
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("BZOJ1018.in", "r", stdin);
freopen("BZOJ1018.out", "w", stdout);
#endif
read(c);
char opt[1 << 4];
Build(1, 1, c);
while(1)
{
read(opt);
if(opt[0] == 'E')
return 0;
int r1, c1, r2, c2;
read(r1), read(c1), read(r2), read(c2);
if(c1 > c2)
swap(r1, r2), swap(c1, c2);
if(opt[0] == 'O')
Change(1, 1, 1, c, r1 - 1, c1, r2 - 1, c2);
else if(opt[0] == 'C')
Change(0, 1, 1, c, r1 - 1, c1, r2 - 1, c2);
else
Ask(r1 - 1, c1, r2 - 1, c2);
}
}