2
Open 1 1 1 2
Open 1 2 2 2
Ask 1 1 2 2
Ask 2 1 2 2
Exit
Y
N
Sol
题意:给你一个2行C列的网格,网格上的每一个点都表示一个城市,每两个相邻的城市之间都有一条道路。
如图:
(每个圆圈代表一个城市)
刚开始都是不通的。然后给你一些指令可以使一些道路畅通可以经过。询问两点之间能否经过。
解题思路:
从一个城市走到另一个城市,有四种方案。
———————————————————————————————————————————————————————————————————————
假设询问(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),只能右上下走,是否有路
(好吧,因为我觉得这个解释比我的解释清楚得多,如果有看不懂的地方可以在我博客下方留言)
这三个数据都可以用线段树维护。
—————————————————————————————————————————————————————————
每个节点表示区间[s,t]的矩形,并且记录8个变量:U,D,l,r,u,d,p,q。
其中,mid为(s+t)/2:
U:第一行mid,mid+1两列之间是否联通
D:第二行mid,mid+1两列之间是否联通
l:s1,s3是否联通
r:s2,s4是否联通
u:s1,s2是否联通
d:s3,s4是否联通
q:s1,s4是否联通
p:s3,s2是否联通
—————————————————————————————————————————————————————————————
发现如果s=t的话,也就是说只有上下两个城市,那么U,D,u,d这4个变量就没有意义了,我们将它们全部赋值为1,避免一些不必要的麻烦。另外注意线段树中的每一个节点只维护这个矩形四个角上的点的连通性,并且我们并不关心它的路径是什么,只考虑使其连通的路径存在于当前矩形的情况。
详情请看代码(附有不详细注释)
#include
#include
using namespace std;
const int maxn=100010;
struct tree{int l,r;}tree[maxn*4];
struct node
{
int U,D,l,r,u,d,p,q;
/*U:第一行mid,mid+1两列之间是否联通
D:第二行mid,mid+1两列之间是否联通
l:s1,s3是否联通
r:s2,s4是否联通
u:s1,s2是否联通
d:s3,s4是否联通
q:s1,s4是否联通
p:s3,s2是否联通*/
}w[maxn*4];
int c,r1,r2,c1,c2;
char s[10];
int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
//这一段请结合图看(精华)
void merge(node &k,node l,node r)
{
k.l=l.l|(l.u&k.U&r.l&k.D&l.d);
k.r=r.r|(r.u&k.U&l.r&k.D&r.d);
k.u=(l.u&k.U&r.u)|(l.q&k.D&r.p);
k.d=(l.d&k.D&r.d)|(l.p&k.U&r.q);
k.q=(l.q&k.D&r.d)|(l.u&k.U&r.q);
k.p=(l.p&k.U&r.u)|(l.d&k.D&r.p);
}
//建树
void build(int k,int l,int r)
{
tree[k].l=l;tree[k].r=r;
if(l==r)
{
w[k].u=w[k].d=w[k].U=w[k].D=1;
return ;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
}
//当两个城市在同一行时
void updater(int k,int x,int t,int val)
{
int mid=(tree[k].l+tree[k].r)>>1;
if(x==mid)
{
if(t==1)w[k].U=val;
else w[k].D=val;
merge(w[k],w[k<<1],w[k<<1|1]);
return ;
}
if(x<=mid)updater(k<<1,x,t,val);
else updater(k<<1|1,x,t,val);
merge(w[k],w[k<<1],w[k<<1|1]);
}
//当两个城市不在同一行时
void updatec(int k,int x,int val)
{
if(tree[k].l==tree[k].r)
{
w[k].l=w[k].r=w[k].p=w[k].q=val;
return ;
}
int mid=(tree[k].l+tree[k].r)>>1;
if(x<=mid)updatec(k<<1,x,val);
else updatec(k<<1|1,x,val);
merge(w[k],w[k<<1],w[k<<1|1]);
}
//查询答案
node find(int k,int s,int t)
{
int l=tree[k].l,r=tree[k].r,mid=(l+r)>>1;
if(s<=l&&r<=t)return w[k];
if(t<=mid)return find(k<<1,s,t);
else if(s>mid)return find(k<<1|1,s,t);
else
{
node res=w[k];
merge(res,find(k<<1,s,t),find(k<<1|1,s,t));
return res;
}
}
int main()
{
c=read();
build(1,1,c);
while(scanf("%s",s)!=EOF)
{
if(s[0]=='E')break;
r1=read();c1=read();r2=read();c2=read();
if(c1>c2)swap(c1,c2),swap(r1,r2);//保证第一个城市在第二个城市的左边
if(s[0]=='O')
{
if(r1==r2)updater(1,c1,r1,1);//如果这两个城市在同一行进行在同一行的操作
else updatec(1,c1,1); //不在同一行进行不在同一行的操作
}
if(s[0]=='C')
{
if(r1==r2)updater(1,c1,r1,0);
else updatec(1,c1,0);
//同上
}
if(s[0]=='A')
{
node l=find(1,1,c1),x=find(1,c1,c2),r=find(1,c2,c);
int ans;
if (r1==1&&r2==1)ans=x.u|(l.r&x.p)|(x.q&r.l)|(l.r&x.d&r.l);
//如s1到s2
if (r1==1&&r2==2)ans=x.q|(l.r&x.d)|(x.u&r.l)|(l.r&x.p&r.l);
//如s1到s4
if (r1==2&&r2==1)ans=x.p|(l.r&x.u)|(x.d&r.l)|(l.r&x.q&r.l);
//如s2到s3
if (r1==2&&r2==2)ans=x.d|(l.r&x.q)|(x.p&r.l)|(l.r&x.u&r.l);
//如s2到s4
puts(ans?"Y":"N");
}
}
return 0;
}