哇这个线段树好神啊!!
用线段树维护图连通性,每个节点开个二维数组
ai,j
a
i
,
j
表示这个区间最左面的第
i
i
行能不能走到最右面的第行,同时记录一下最左最右第一行与第二行是否都能互相走到
这六个东西可以用线段树维护啊(看代码)!
细节有点多…具体看代码吧…
注释说的很清楚(自信)
代码如下:
#include<algorithm>
#include<cstring>
#include<ctype.h>
#include<cstdio>
#define N 100050
using namespace std;
inline int read(){
int x=0,f=1;char c;
do c=getchar(),f=c=='-'?-1:f; while(!isdigit(c));
do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
return x*f;
}
bool b1[N],b2[N][3];///b1是纵向的边 b2是横向的边(向右指的)
struct Status{
bool b[2][2],lb,rb;
int l,r;
inline void init(int x,int y){
l=x;r=y;
memset(b,false,sizeof b);
lb=rb=false;
return;
}
Status operator + (const Status k) const{
Status tmp;
tmp.l=l;tmp.r=k.r;
tmp.lb=lb|(b2[r][1]&b2[r][2]&b[0][0]&b[1][1]&k.lb);
///合并当前状态和k状态,每个情况yy一下就非常好理解了
tmp.rb=k.rb|(b2[r][1]&b2[r][2]&k.b[0][0]&k.b[1][1]&rb);
tmp.b[0][0]=(b[0][0]&k.b[0][0]&b2[r][1])|(b[0][1]&k.b[1][0]&b2[r][2]);
tmp.b[0][1]=(b[0][0]&k.b[0][1]&b2[r][1])|(b[0][1]&k.b[1][1]&b2[r][2]);
tmp.b[1][0]=(b[1][0]&k.b[0][0]&b2[r][1])|(b[1][1]&k.b[1][0]&b2[r][2]);
tmp.b[1][1]=(b[1][0]&k.b[0][1]&b2[r][1])|(b[1][1]&k.b[1][1]&b2[r][2]);
return tmp;
}
};
struct Node{
Node *ls,*rs;
Status a;///存的状态
int l,r;
inline void maintain(){
if(l==r) return;
a=ls->a+rs->a;
return;
}
}*root=new Node;
void maketree(int l,int r,Node *x){
x->l=l;x->r=r;
x->a.init(l,r);
if(l==r){
x->a.b[0][0]=x->a.b[1][1]=true;///建树,一定要将单个点的这个赋成true
return;
}
int mid=(l+r)>>1;
maketree(l,mid,x->ls=new Node);maketree(mid+1,r,x->rs=new Node);
x->maintain();
return;
}
void Modify_Up(int x,bool v,Node *k){///修改竖着的边,就是将线段树的单个节点的连通性修改一下
if(k->l==k->r){
k->a.b[0][1]=k->a.b[1][0]=v;
k->a.lb=k->a.rb=v;
return;
}
int mid=(k->l+k->r)>>1;
if(x<=mid) Modify_Up(x,v,k->ls);
else Modify_Up(x,v,k->rs);
k->maintain();
return;
}
void Update(int x,Node *k){///因为更改一条x到y的横着的边只会影响最多两串,只要到底部然后回溯时维护就好了
if(k->l==k->r) return;
int mid=(k->l+k->r)>>1;
if(x<=mid) Update(x,k->ls);
else Update(x,k->rs);
k->maintain();
return;
}
Status Query(int x,int y,Node *k){///询问,就是将所有子段的信息合并
if(k->l>=x && k->r<=y){
return k->a;
}
int mid=(k->l+k->r)>>1;
if(y<=mid) return Query(x,y,k->ls);
else if(x>mid) return Query(x,y,k->rs);
else return Query(x,y,k->ls)+Query(x,y,k->rs);
}
int n,x,y,x1,y1;
char c[5];
bool ans;
int main(){
n=read();
maketree(1,n,root);
while(""){
scanf("%s",c+1);
if(c[1]=='O'){
x=read();y=read();x1=read();y1=read();
if(y==y1){
b1[y]=true;
Modify_Up(y,true,root);///修改纵向的边,在线段树上修改
}
else{
if(y>y1) swap(y,y1),swap(x,x1);///同下
b2[y][x]=true;
Update(y,root);
Update(y1,root);
}
}
else if(c[1]=='C'){
x=read();y=read();x1=read();y1=read();
if(y==y1){///同上
b1[y]=false;
Modify_Up(y,false,root);
}
else{
if(y>y1) swap(y,y1),swap(x,x1);
b2[y][x]=false;///修改横向的边,只要重新维护一遍就可以了
Update(y,root);
Update(y1,root);
}
}
else if(c[1]=='A'){
x=read();y=read();x1=read();y1=read();
if(y>y1) swap(x,x1),swap(y,y1);
Status l=Query(1,y,root),k=Query(y,y1,root),r=Query(y1,n,root);
///注意这里,l是左面的区域,目的是统计从左面绕过去的方案,r同理
if(x==1 && x1==1) ans=k.b[0][0] | (l.rb & k.b[1][0]) | (r.lb & k.b[0][1]) | (r.lb & l.rb & k.b[1][1]);
///每个情况判断一下..还是挺恶心的
if(x==1 && x1==2) ans=k.b[0][1] | (l.rb & k.b[1][1]) | (r.lb & k.b[0][0]) | (r.lb & l.rb & k.b[1][0]);
if(x==2 && x1==1) ans=k.b[1][0] | (l.rb & k.b[0][0]) | (r.lb & k.b[1][1]) | (r.lb & l.rb & k.b[0][1]);
if(x==2 && x1==2) ans=k.b[1][1] | (l.rb & k.b[0][1]) | (r.lb & k.b[1][0]) | (r.lb & l.rb & k.b[0][0]);
puts(ans?"Y":"N");
}
else if(c[1]=='E') return 0;
}
return 0;
}