链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1018
理论上是可以直接线段树分治的,直接维护点的连通性,但这样是
l
o
g
2
log^2
log2的
有更优秀的做法
我们考虑对于这种网格图,我们是可以分成两个部分来考虑连通性的
对于每一个部分,我们要维护的是左右边界两两之间的连通性,不难发现,设宽为n,那么状态数是
n
2
n^2
n2的
至于部分之间的合并,我们可以枚举从哪一列走过去,然后用两边的左右边界连通性合并即可,
这里只给两列的意思就是让你方便讨论,这样就只有两个情况了,暴力讨论就好了
显然可以用线段树进行维护
因为支持合并,我们线段树还要维护在mid这个位置,两列的连通性如何
然后就没什么了
修改就暴力修改,询问的话要注意还要查询左右两边剩下的点再讨论一次,因为我们线段树查询的是区间,并不可以维护经过左右两边的情况
如果数据出得更大的话可以用一样的方法来做,但常数会变大,讨论应该可以用for代替
CODE:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=100005;
int C;
struct qq
{
int L,R;//哪一个矩阵
int U,D,l,r,u,d,q,p;
int s1,s2;
}tr[N<<2];int num=0;
void bt (int l,int r)
{
int a=++num;
tr[a].L=l;tr[a].R=r;
if (l==r)
{
tr[a].U=tr[a].D=tr[a].u=tr[a].d=1;
return ;
}
int mid=(l+r)>>1;
tr[a].s1=num+1;bt(l,mid);
tr[a].s2=num+1;bt(mid+1,r);
}
/*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是否联通*/
void Merge (qq &now,qq s1,qq s2)
{
now.l=s1.l|(s1.u&now.U&s2.l&now.D&s1.d);
now.r=s2.r|(s2.u&now.U&s1.r&now.D&s2.d);
now.u=(s1.u&now.U&s2.u)|(s1.q&now.D&s2.p);
now.d=(s1.d&now.D&s2.d)|(s1.p&now.U&s2.q);
now.q=(s1.u&now.U&s2.q)|(s1.q&now.D&s2.d);
now.p=(s1.d&now.D&s2.p)|(s1.p&now.U&s2.u);
}
void update (int now,int x,int y,int z)//修改
{
int l=tr[now].L,r=tr[now].R;
// printf("YES:%d %d %d %d %d\n",l,r,x,y,z);
int mid=(l+r)>>1;
int s1=tr[now].s1,s2=tr[now].s2;
if (x==mid)//修改的刚刚好是中轴线
{
if (y==1) tr[now].U=z;
else tr[now].D=z;
Merge(tr[now],tr[s1],tr[s2]);
return ;
}
if (x<=mid) update(s1,x,y,z);
else update(s2,x,y,z);
Merge(tr[now],tr[s1],tr[s2]);
}
void update1 (int now,int x,int z)
{
int l=tr[now].L,r=tr[now].R;
int mid=(l+r)>>1;
int s1=tr[now].s1,s2=tr[now].s2;
if (l==r)
{
tr[now].l=tr[now].r=tr[now].p=tr[now].q=z;
return ;
}
if (x<=mid) update1(s1,x,z);
else update1(s2,x,z);
Merge(tr[now],tr[s1],tr[s2]);
}
qq get (int now,int x,int y)
{
int l=tr[now].L,r=tr[now].R;
int mid=(l+r)>>1;
int s1=tr[now].s1,s2=tr[now].s2;
if (x==l&&y==r) return tr[now];
if (y<=mid) return get(s1,x,y);
else if (x>mid) return get(s2,x,y);
else
{
qq shen=tr[now];
Merge(shen,get(s1,x,mid),get(s2,mid+1,y));
return shen;
}
}
int main()
{
scanf("%d",&C);
bt(1,C);
while (true)
{
char ss[5];
int r1,c1,r2,c2;
scanf("%s",ss);
if (ss[0]=='E') break;
scanf("%d%d%d%d",&r1,&c1,&r2,&c2);
if (c1>c2) swap(c1,c2),swap(r1,r2);
if (ss[0]=='A')
{
/*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是否联通*/
qq l=get(1,1,c1),x=get(1,c1,c2),r=get(1,c2,C);
int ans=0;
if (r1==1&&r2==1)//在同一行
ans=x.u|(l.r&x.p)|(x.q&r.l)|(l.r&x.d&r.l);
if (r1==1&&r2==2)//不在同一行
ans=x.q|(l.r&x.d)|(x.u&r.l)|(l.r&x.p&r.l);
if (r1==2&&r2==1)
ans=x.p|(l.r&x.u)|(x.d&r.l)|(l.r&x.q&r.l);
if (r1==2&&r2==2)
ans=x.d|(l.r&x.q)|(x.p&r.l)|(l.r&x.u&r.l);
if (ans==1) printf("Y\n");
else printf("N\n");
}
else//连通
{
if (r1==r2)//修改的横着的
update(1,c1,r1,ss[0]=='O');
else//修改竖着的
update1(1,c1,ss[0]=='O');
}
}
return 0;
}