传送门
我自从月考完了,就在调这个题。。
首先,先看一下官方题解。
算法:线段树
先贴一个连接:https://www.luogu.org/problemnew/show/U16516
这个是分测试点,可以看自己对了多少分。
然后开始算法解释。
一部分一部分来。
这里是data的声明,初始化(很重要),还有输出调试。
data里的l,r代表所管理的区间
数组a代表存储的联通信息
具体联通信息所代表的含义如下图:
其中圆中的数字代表节点编号
边上的数字代表连通性标号
const int N=1e5+5;
struct data{
int l,r;
int a[7];
data(){
l=r=0;
memset(a,0,sizeof(a));
}
}f[N];
inline void print(const data& x){
cout<<endl;
cout<<"data of "<<x.l<<" to "<<x.r<<endl;
char s[10]="f[ ] = ";
for(int i=1;i<=6;i++)s[2]=i+'0',printf("%s%d\n",s,x.a[i]);
cout<<endl;
}
这里是将边初始化到邻接矩阵里,然后跑floyd
int n;
int g[5][5],e1[N],e2[N],e3[N];//g为邻接矩阵,e1一排横边,e2二排横边,e3竖边
inline void init(int x){
memset(g,0,sizeof(g));
if(e1[x])g[1][2]=g[2][1]=1;
if(e2[x])g[3][4]=g[4][3]=1;
if(e3[x])g[1][3]=g[3][1]=1;
if(e3[x+1])g[2][4]=g[4][2]=1;
}
inline void floyd(){
for(int k=1;k<=4;k++)
for(int i=1;i<=4;i++)
for(int j=1;j<=4;j++)
g[i][j]|=g[i][k]&g[k][j];
}
这是利用邻接矩阵初始化一个data信息
第二个merge是合并两个data
inline void color(data &x){
x.a[1]=g[1][3];
x.a[2]=g[1][2];
x.a[3]=g[2][4];
x.a[4]=g[3][4];
x.a[5]=g[1][4];
x.a[6]=g[2][3];
}
inline data merge(const data& a,const data& b){
if(!a.l)return b;
if(!b.l)return a;
data c;
c.l=a.l;c.r=b.r;
c.a[1]=a.a[1]|(a.a[2]&b.a[1]&a.a[4]);
c.a[2]=(a.a[2]&b.a[2])|(a.a[5]&b.a[6]);
c.a[3]=b.a[3]|(b.a[2]&a.a[3]&b.a[4]);
c.a[4]=(a.a[4]&b.a[4])|(a.a[6]&b.a[5]);
c.a[5]=(a.a[2]&b.a[5])|(a.a[5]&b.a[4]);
c.a[6]=(a.a[4]&b.a[6])|(a.a[6]&b.a[2]);
return c;
}
以下是线段树的pushup和build
inline void pushup(int rt){
f[rt]=merge(f[rt<<1],f[rt<<1|1]);
}
inline void build(int rt,int l,int r){
f[rt].l=l;f[rt].r=r;
if(l==r){
init(l);
floyd();
color(f[rt]);
return;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
pushup(rt);
}
以下是线段树的update和query
int L,R,pos;
inline void update(int rt,int l,int r){
if(l==r){
init(l);
floyd();
color(f[rt]);
return;
}
int mid=(l+r)>>1;
if(pos<=mid)update(rt<<1,l,mid);
else update(rt<<1|1,mid+1,r);
pushup(rt);
}
data all;//all代表全联通data,后面有用
inline data query(int rt,int l,int r){
if(L<=l&&r<=R)return f[rt];
int mid=(l+r)>>1;
data ans;
if(L<=mid)ans=merge(ans,query(rt<<1,l,mid));
if(mid+1<=R)ans=merge(ans,query(rt<<1|1,mid+1,r));
// cout<<"ask of "<<l<<" to "<<r<<endl;
// print(ans);
return ans;
}
以下为主程序的一部分
int main(){
for(int i=1;i<=6;i++)all.a[i]=1;
n=read()-1;
build(1,1,n);
int X1,Y1,X2,Y2;
while(1){
char opt[10];
scanf("%s",opt);
if(opt[0]=='E')break;
X1=read();Y1=read();X2=read();Y2=read();
/*
some code
*/
}
return 0;
}
以下为Open操作:
if(opt[0]=='O'){
if(X1!=X2){
e3[Y1]=1;
pos=Y1,update(1,1,n);
if(Y1>1)pos=Y1-1,update(1,1,n);
}
else{
if(Y1>Y2)swap(Y1,Y2);
if(X1==1)e1[Y1]=1;
else e2[Y1]=1;
pos=Y1,update(1,1,n);
}
}
接下来是Close操作,与Open类似:
else if(opt[0]=='C'){
if(X1!=X2){
e3[Y1]=0;
pos=Y1,update(1,1,n);
if(Y1>1)pos=Y1-1,update(1,1,n);
}
else{
if(Y1>Y2)swap(Y1,Y2);
if(X1==1)e1[Y1]=0;
else e2[Y1]=0;
pos=Y1,update(1,1,n);
}
}
然后是Ask,超级长(为了避免错误,进行了许多讨论):
else{
bool flag=0;
if(Y1==Y2){
if(X1==X2)flag=1;
else{
if(Y1==1){
L=1,R=n;
data ans=query(1,1,n);
if(ans.a[1])flag=1;
}
else if(Y1==n+1){
L=1,R=n;
data ans=query(1,1,n);
if(ans.a[3])flag=1;
}
else{
L=1,R=Y1-1;
data ans1=query(1,1,n);
if(ans1.a[3])flag=1;
L=Y1,R=n;
data ans2=query(1,1,n);
if(ans2.a[1])flag=1;
}
}
}
else{
if(Y1>Y2)swap(Y1,Y2),swap(X1,X2);
L=Y1,R=Y2-1;
data ans=query(1,1,n);
if(X1==1&&X2==1)flag=ans.a[2];
else if(X1==1&&X2==2)flag=ans.a[5];
else if(X1==2&&X2==1)flag=ans.a[6];
else flag=ans.a[4];
data ans1,ans2;
L=1,R=Y1-1;
if(L<=R)ans1=query(1,1,n);
else ans1=all;
L=Y2,R=n;
if(L<=R)ans2=query(1,1,n);
else ans2=all;
if(X1==1&&X2==1)flag|=ans.a[4]&ans1.a[3]&ans2.a[1];
else if(X1==1&&X2==2)flag|=ans.a[6]&ans1.a[3]&ans2.a[1];
else if(X1==2&&X2==1)flag|=ans.a[5]&ans1.a[3]&ans2.a[1];
else flag|=ans.a[2]&ans1.a[3]&ans2.a[1];
}
if(flag)puts("Y");
else puts("N");
}
这个题就是个代码题,太难调了。。
还有线段树真的好神呀!
完结撒花!