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

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));
				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.第一次知道线段树还能维护连通性,感觉很神奇~

你可能感兴趣的:(线段树,OI,bzoj)