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
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
网站上的题解:
假设询问(r1,1)到(r2,2)(c2>=c1)的连通性,我们对最复杂的连通方式进行分析:
将路线分成3个部分,很容易发现,路线只有几种可能:
1. 从(r1,1)直接一直向右、上、下走直接到(r2,2)
2. 先从(r1,1)到(r1,2),再从(r1,2)直接一直向右、上、下走直接到(r2,2)
3. 从(r1,1)直接一直向右、上、下走直接到(r2,1),再从(r2,1)到(r2,2)
4. 先从(r1,1)到(r1,2),再从(r1,2)直接一直向右、上、下走直接到(r2,1) ,再从(r2,1)到(r2,2)
其中,(r1,1)到(r1,2)的方法只能是一直向左走到某个地方,然后想下,再一直向右走到(r1,2)
同理,(r2,1)到(r2,2)的方法只能是一直向右走到某个地方,然后想下,再一直向左走到(r2,2)
于是我们只需要能够快速得到如下数据便可以判断(r1,1),(r2,2)是否连通:
1. (r1,1)到(r1,2),只能先左走一段,再向下,再右走一段是否有路
2. (r2,1)到(r2,2),只能先右走一段,再向下,再左走一段是否有路
3. 从(r1,1)到(r2,1),从(r1,1)到(r2,2),从(r1,2)到(r2,1),从(r1,2)到(r2,2),只能右上下走,是否有路
而这3个数据都可以用线段树维护,具体的维护方法请自行思考。
/****************************************************/
这道题确实是一道非常好的题目,与通常做的线段树完全不一样,给了我许多新的思路,在这里再分享另外一篇博客,讲的更详细一些:
戳这里
我的代码如下:
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<string>
#define N 100005
#define mid (l+r>>1)
#define ls (rt<<1)
#define rs (rt<<1|1)
using namespace std;
struct nod{bool g[6];}s[5],tr[N*3];
int n;
bool mp[N*3];
int calc(int x,int y){return x*(n-1)+y;}
void init()
{
scanf("%d",&n);
s[0]=(nod){1,1,0,0,0,0};
s[1]=(nod){1,1,1,1,1,1};
memset(mp,0,sizeof(mp));
memset(tr,0,sizeof(tr));
}
void build(int rt,int l,int r)
{
if(l==r)
{
tr[rt]=s[0];
return;
}
build(ls,l,mid);build(rs,mid+1,r);
}
nod merge(nod a,nod b,bool x,bool y)
{
nod c;
c.g[0]=(a.g[0] && x && b.g[0])|(a.g[4] && y && b.g[5]);
c.g[1]=(a.g[1] && y && b.g[1])|(a.g[5] && x && b.g[4]);
c.g[2]=(a.g[2])|(a.g[0] && x && b.g[2] && y && a.g[1]);
c.g[3]=(b.g[3])|(b.g[0] && x && a.g[3] && y && b.g[1]);
c.g[4]=(a.g[0] && x && b.g[4])|(a.g[4] && y && b.g[1]);
c.g[5]=(b.g[0] && x && a.g[5])|(b.g[5] && y && a.g[1]);
return c;
}
void insert(int rt,int l,int r,int x1,int y1,int x2,int y2,bool c)
{
int y=min(y1,y2);
if(x1==x2 && y==mid)
{
mp[calc(x1,y)]=c;
tr[rt]=merge(tr[ls],tr[rs],mp[calc(0,mid)],mp[calc(1,mid)]);
return;
}
else if(x1!=x2 && l==r)
{
mp[2*(n-1)+y]=c;
tr[rt]=s[c];
return;
}
if(y<=mid) insert(ls,l,mid,x1,y1,x2,y2,c);
if(y>mid) insert(rs,mid+1,r,x1,y1,x2,y2,c);
tr[rt]=merge(tr[ls],tr[rs],mp[calc(0,mid)],mp[calc(1,mid)]);
}
nod query(int rt,int l,int r,int L,int R)
{
if(l>=L && r<=R) return tr[rt];
if(R<=mid) return query(ls,l,mid,L,R);
if(L>mid) return query(rs,mid+1,r,L,R);
else return merge(query(ls,l,mid,L,R),query(rs,mid+1,r,L,R),mp[calc(0,mid)],mp[calc(1,mid)]);
}
void ask(int x1,int y1,int x2,int y2)
{
bool ans;
if(y1>y2) swap(x1,x2),swap(y1,y2);
s[2]=query(1,1,n,y1,y2);
s[3]=query(1,1,n,1,y1);
s[4]=query(1,1,n,y2,n);
if(x1==x2) ans=(s[2].g[x1])|(s[3].g[3] && s[2].g[4+x1^1])|(s[4].g[2] && s[2].g[4+x1])|(s[3].g[3] && s[4].g[2] && s[2].g[x1^1]);
if(x1!=x2) ans=(s[2].g[4+x1])|(s[3].g[3] && s[2].g[x1^1])|(s[4].g[2] && s[2].g[x1])|(s[3].g[3] && s[4].g[2] && s[2].g[4+x1^1]);
if(ans) puts("Y");
else puts("N");
}
void solve()
{
char ch[10];
int x1,y1,x2,y2;
while(~scanf("%s",ch) && ch[0]!='E')
{
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);x1--,x2--;
if(ch[0]=='O') insert(1,1,n,x1,y1,x2,y2,1);
if(ch[0]=='C') insert(1,1,n,x1,y1,x2,y2,0);
if(ch[0]=='A') ask(x1,y1,x2,y2);
}
}
int main()
{
init();
build(1,1,n);
solve();
return 0;
}