【BZOJ 1018】 [SHOI2008]堵塞的交通traffic

来源:互联网 发布:淘宝手机端短连接 编辑:程序博客网 时间:2024/06/02 19:49

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

对于每个查询,输出一个“Y”或“N”。

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));elseUpdate2(1,y1);break;case 'C':                                Modi(x1,y1,x2,y2,0);                                if (x1==x2)Update1(1,min(y1,y2),max(y1,y2));elseUpdate2(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.第一次知道线段树还能维护连通性,感觉很神奇~

1 0