1018: [SHOI2008]堵塞的交通traffic
Time Limit: 3 Sec
Memory Limit: 162 MB
Submit: 1811
Solved: 580
[ Submit][ Status]
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”作为结束。我们假设在一开始所有的道路都是堵塞的。 对30%测试数据,我们保证C小于等于1000,信息条数小于等于1000; 对100%测试数据,我们保证 C小于等于100000,信息条数小于等于100000。
Output
Sample Input
2
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
用线段树维护连通性,非常考验分类讨论能力的码农题!
对于一段区间l-r,线段树要维护的是左上左下右上右下这四个地方的连通性,在我的程序中:
(1,1) (1,2)
(2,1) (2,2)
h1:(1,1)-->(1,2)
h2:(2,1)-->(2,2)
s1:(1,1)-->(2,1)
s2:(1,2)-->(2,2)
x1:(1,1)-->(2,2)
x2:(1,2)-->(2,1)
要维护这六个信息,同时,因为要区间合并,用一个l[x][y][k]表示(x,y)这个位置与周围的连通情况(其中k=1,2,3,4分别是与上下左右的连通情况)。
接下来就是处理修改了:一开始考虑合并的时候,感觉路线非常非常多。其实要谨记一点:
要合并的两个区间该连的都已经连好了,当前区间的合并只要考虑使用两个区间的交界就可以了!
(具体见代码Update)
最后是查询l-r的连通情况:
从l-r可能直接过去,也可能从l到前面再到r,也可能从r到后面再到l。
因此预处理出1-l,l-r,r-n这三个区间的连通情况,分别记为a1,a2,a3。
若a1.s2连通,则a2.s1连通;a3.s1连通,则a2.s2连通。
然后根据纵坐标的不同讨论一下就可以了(一定要考虑周全啊!!)。
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cmath>
using namespace std;
struct Node
{
int l,r,h1,h2,s1,s2,x1,x2;
}a[500005];
int n,l[3][100005][5];
void Build(int x,int l,int r)
{
a[x].l=l,a[x].r=r;
a[x].h1=a[x].h2=a[x].s1=a[x].s2=a[x].x1=a[x].x2=0;
if (l==r)
{
a[x].h1=a[x].h2=1;
return;
}
int m=(l+r)>>1;
Build(x<<1,l,m);
Build((x<<1)+1,m+1,r);
}
void Modi(int x1,int y1,int x2,int y2,int k)
{
if (x1==x2)
{
if (y1>y2) swap(y1,y2);
l[x1][y1][4]=l[x2][y2][3]=k;
}
else
{
if (x1>x2) swap(x1,x2);
l[x1][y1][2]=l[x2][y2][1]=k;
}
}
void Modis(int x)
{
a[x].s1=l[1][a[x].l][2];
a[x].s2=l[1][a[x].r][2];
a[x].x1=a[x].x2=0;
if (a[x].s1|a[x].s2)
a[x].x1=a[x].x2=1;
}
Node Merge(Node x,Node y)
{
if (x.l==y.l&&x.r==y.r) return x;
Node k;
int p1=l[1][x.r][4],p2=l[2][x.r][4];
k.h1=(x.h1&p1&y.h1)|(x.x1&p2&y.x2);
k.h2=(x.h2&p2&y.h2)|(x.x2&p1&y.x1);
k.s1=x.s1|(x.h1&p1&y.s1&p2&x.h2);
k.s2=y.s2|(y.h1&p1&x.s2&p2&y.h2);
k.x1=(x.h1&p1&y.x1)|(x.x1&p2&y.h2);
k.x2=(y.x2&p2&x.h2)|(y.h1&p1&x.x2);
k.l=x.l,k.r=y.r;
return k;
}
void Update1(int x,int l,int r)
{
int m=(a[x].l+a[x].r)>>1;
if (l==m)
{
a[x]=Merge(a[x<<1],a[(x<<1)+1]);
return;
}
if (l<m) Update1(x<<1,l,r);
else Update1((x<<1)+1,l,r);
a[x]=Merge(a[x<<1],a[(x<<1)+1]);
}
void Update2(int x,int k)
{
if (a[x].l==a[x].r&&a[x].l==k)
{
Modis(x);
return;
}
int m=(a[x].l+a[x].r)>>1;
if (k<=m) Update2(x<<1,k);
else Update2((x<<1)+1,k);
a[x]=Merge(a[x<<1],a[(x<<1)+1]);
}
Node Find(int x,int l,int r)
{
if (a[x].l>=l&&a[x].r<=r)
return a[x];
int m=(a[x].l+a[x].r)>>1;
if (r<=m) return Find(x<<1,l,r);
if (l>m) return Find((x<<1)+1,l,r);
return Merge(Find(x<<1,l,r),Find((x<<1)+1,l,r));
}
bool Query(int x1,int y1,int x2,int y2)
{
Node a1=Find(1,1,y1),a2=Find(1,y1,y2),a3=Find(1,y2,n);
a2.s1^=a1.s2,a2.s2^=a3.s1;
if (x1==x2)
{
if (x1==1) return a2.h1|(a2.s1&a2.h2&a2.s2)|(a2.s1&a2.x2)|(a2.x1&a2.s2);
else return a2.h2|(a2.s1&a2.h1&a2.s2)|(a2.s1&a2.x1)|(a2.s2&a2.x2);
}
if (x1<x2) return a2.x1|(a2.s1&a2.h2)|(a2.h1&a2.s2);
return a2.x2|(a2.s1&a2.h1)|(a2.h2&a2.s2);
}
int main()
{
scanf("%d",&n);
Build(1,1,n);
char s[10];
while (scanf("%s",s)!=EOF)
{
if (s[0]=='E') break;
int x1,y1,x2,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
switch(s[0])
{
case 'O':
Modi(x1,y1,x2,y2,1);
if (x1==x2)
Update1(1,min(y1,y2),max(y1,y2));
else
Update2(1,y1);
break;
case 'C':
Modi(x1,y1,x2,y2,0);
if (x1==x2)
Update1(1,min(y1,y2),max(y1,y2));
else
Update2(1,y1);
break;
case 'A':
if (y1>y2) swap(x1,x2),swap(y1,y2);
if (Query(x1,y1,x2,y2)) printf("Y\n");
else printf("N\n");
break;
}
}
return 0;
}
感悟:
1.艰难AC史:
建树的时候对于l=r的结点,h1和h2赋值为1;
Merge的时候,一定要给这个结点的lr赋值,否则会RE;
查询的时候我以为每个区间都可以分成两个线段树上的区间。。。显然不是的。。
查询的时候对于x1=x2的情况,因为a1和a3可能改变a2的s1和s2,因此需要讨论的路线!!!
2.第一次知道线段树还能维护连通性,感觉很神奇~