Description
有一天,由于某种穿越现象作用,你来到了传说中的小人国。小人国的布局非常奇特,整个国家的交通系统可以被看成是一个
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”作为结束。我们假设在一开始所有的道路都是堵塞的。我们保证
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
HINT
题解:JudgeOnline/upload/201604/sol(4).rar
Source
思路
首先对于一个只有一行的情况,可以很容易的用线段树维护。
但是对于两行的情况,就需要用一些奇怪的技巧了。
考虑下面这个图:
从
r1,1
到
r2,2
可以先从
r1,1
到
r2,1
,再到
r1,2
,最后是
r2,2
。
那么可以维护
r1,1
到
r2,1
的联通性,
r2,1
到
r1,2
的联通性,
r1,2
到
r2,2
的联通性。
具体一点,假设有区间为:
q ———
r
z ———v
那么需要维护
q
,
那么更新时就通过这个更新,还有区间
mid
到
mid+1
的联通性(第一列和第二列)就好了。
询问就按照上面的查询方法写。
代码
其实是可以不用写这么多的,只是我写的太长了……
#include <cstdio>
#include <algorithm>
const int maxn=100000;
struct data
{
int we,xc,qr,zv,qz,rv,qv,rz;
//解释一下变量名的含义:
//将键盘上左手边的一块看成一个矩形
//就是
//qwer
//asdf
//zxcv
//把q,r,z,v看成矩形的4个顶点
//w和x看成是中间点
//e和c看成是中间点的编号+1位置的点
//连起来写的两个字符表示两个位置的联通性
};
struct segment_tree
{
data val[(maxn<<2)+10];
inline int merge(data &d,data l,data r)
//精华部分,自己画个图就明白了
//意思是将l和r区间合并成一个区间d
{
d.qz=l.qz|(l.qr&d.we&r.qz&d.xc&l.zv);
d.rv=r.rv|(r.qr&d.we&l.rv&d.xc&r.zv);
d.qr=(l.qv&d.xc&r.rz)|(l.qr&d.we&r.qr);
d.zv=(l.zv&d.xc&r.zv)|(l.rz&d.we&r.qv);
d.qv=(l.qv&d.xc&r.zv)|(l.qr&d.we&r.qv);
d.rz=(l.zv&d.xc&r.rz)|(l.rz&d.we&r.qr);
return 0;
}
int build(int now,int left,int right)
//建树
{
if(left==right)
{
val[now].qr=val[now].zv=val[now].we=val[now].xc=1;
val[now].qz=val[now].rv=val[now].qv=val[now].rz=0;
return 0;
}
int mid=(left+right)>>1;
val[now].we=val[now].xc=0;
build(now<<1,left,mid);
build(now<<1|1,mid+1,right);
merge(val[now],val[now<<1],val[now<<1|1]);
return 0;
}
int rchange(int now,int left,int right,int pos,int cval)
//将(1,pos)这个点与(2,pos)这个点的联通性变成cval
{
if(left==right)//说明当前这个位置就是pos,直接修改
{
val[now].qz=val[now].rv=val[now].qv=val[now].rz=cval;
return 0;
}
int mid=(left+right)>>1;
if(pos<=mid)//寻找pos
{
rchange(now<<1,left,mid,pos,cval);
}
else
{
rchange(now<<1|1,mid+1,right,pos,cval);
}
merge(val[now],val[now<<1],val[now<<1|1]);//更新这个点的val
return 0;
}
int uchange(int now,int left,int right,int pos,int cval)
//将(1,pos)这个点和(1,pos+1)这个点之间的联通性修改为cval
{
int mid=(left+right)>>1;
if(mid==pos)//如果中点就是pos,直接修改w->e的值
{
val[now].we=cval;
merge(val[now],val[now<<1],val[now<<1|1]);
return 0;
}
if(pos<=mid)//寻找pos
{
uchange(now<<1,left,mid,pos,cval);
}
else
{
uchange(now<<1|1,mid+1,right,pos,cval);
}
merge(val[now],val[now<<1],val[now<<1|1]);
return 0;
}
int dchange(int now,int left,int right,int pos,int cval)
//将(2,pos)这个点和(2,pos+1)这个点的联通性修改为cval
{
int mid=(left+right)>>1;
if(mid==pos)//如果中点就是pos,那么将x->c的值修改
{
val[now].xc=cval;
merge(val[now],val[now<<1],val[now<<1|1]);
return 0;
}
if(pos<=mid)//寻找pos
{
dchange(now<<1,left,mid,pos,cval);
}
else
{
dchange(now<<1|1,mid+1,right,pos,cval);
}
merge(val[now],val[now<<1],val[now<<1|1]);//更新这个点的val
return 0;
}
data query(int now,int left,int right,int s,int t)
//询问s到t区间的联通性(用一个data表示)
{
if((s<=left)&&(right<=t))
{
return val[now];
//如果已经被完全包含,直接返回这个点的val
}
int mid=(left+right)>>1;
if(s>mid)
{
return query(now<<1|1,mid+1,right,s,t);
//如果完全包含于右区间,返回右区间的寻找结果
}
else if(t<=mid)
{
return query(now<<1,left,mid,s,t);
//如果完全含于左区间,返回左区间的寻找结果
}
else
{
data res=val[now];
merge(res,query(now<<1,left,mid,s,t),query(now<<1|1,mid+1,right,s,t));
//两个区间一定是连起来的,直接用val[now]的信息合并左边和右边
return res;
}
}
};
segment_tree st;
int n;
char ch[10];
inline int read()
{
int x=0,f=1;
char ch=getchar();
while((ch<'0')||(ch>'9'))
{
if(ch=='-')
{
f=-f;
}
ch=getchar();
}
while((ch>='0')&&(ch<='9'))
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
int main()
{
n=read();
st.build(1,1,n);//首先建树
while(scanf("%s",ch)!=EOF)
{
if(ch[0]=='E')
{
break;
}
int ax=read(),ay=read(),bx=read(),by=read();
if(ay>by)//保证a一定在b右侧
{
std::swap(ax,bx);
std::swap(ay,by);
}
if(ch[0]=='O')
{
//分情况讨论Open的道路
if(ay==by)
{
st.rchange(1,1,n,ay,1);
}
else if(ax==1)
{
st.uchange(1,1,n,ay,1);
}
else
{
st.dchange(1,1,n,ay,1);
}
}
else if(ch[0]=='C')
{
//分情况讨论Close的道路
if(ay==by)
{
st.rchange(1,1,n,ay,0);
}
else if(ax==1)
{
st.uchange(1,1,n,ay,0);
}
else
{
st.dchange(1,1,n,ay,0);
}
}
else
{
data res=st.query(1,1,n,ay,by),l=st.query(1,1,n,1,ay),r=st.query(1,1,n,by,n);
//res是[ay,by]的联通性,l是[1,ay]的联通性,r是[by,n]的联通性
int ans;
//下面是分情况讨论a和b的横坐标,计算过程就是上面的
if((ax==1)&&(bx==1))
{
ans=res.qr|(l.rv&res.rz)|(r.qz&res.qv)|(l.rv&r.qz&res.zv);
}
else if((ax==1)&&(bx==2))
{
ans=res.qv|(l.rv&res.zv)|(r.qz&res.qr)|(l.rv&r.qz&res.rz);
}
else if((ax==2)&&(bx==1))
{
ans=res.rz|(l.rv&res.qr)|(r.qz&res.zv)|(l.rv&r.qz&res.qv);
}
else
{
ans=res.zv|(l.rv&res.qv)|(r.qz&res.rz)|(l.rv&r.qz&res.qr);
}
if(ans)
{
puts("Y");
}
else
{
puts("N");
}
}
}
return 0;
}