Description
Input
Output
Sample Input
Sample Output
HINT
题解Here!
这题是本蒟蒻接触$LCT$以来见过的最毒瘤的一题。。。
于是我两个晚上就这么搭进去了。。。
首先这题可以线段树搞。
用线段树维护每行的同色块,每个节点套并查集。
记录最上方和最下方的联通性,暴力合并+暴力修改并查集,并记录答案。
并查集的合并比较恶心。。。
用并查集中的$[1,n]$表示上方,$[n+1,2n]$表示下方。
合并时$[2n+1,4n]$用于右侧的上下方并查集。
话说我的线段树常数炒鸡大,卡了好久才卡过去的。。。
附代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<vector> #include<cstring> #define LSON rt<<1 #define RSON rt<<1|1 #define DATA(x) a[x].data #define WHITE(x) a[x].white #define BLACK(x) a[x].black #define LSIDE(x) a[x].l #define RSIDE(x) a[x].r #define MAXN 210 using namespace std; int n,m; int chess[MAXN][MAXN],map[MAXN<<2]; struct Set{ int father[MAXN<<2]; inline void init(){for(int i=0;i<=(n<<2);i++)father[i]=i;} int find(int x){return father[x]==x?x:father[x]=find(father[x]);} inline void uniun(int x,int y){father[find(x)]=find(y);} inline bool check(int x,int y){return (find(x)==find(y));} }; struct Segment_Tree{ Set data; int white,black,l,r; }a[MAXN<<2]; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } void pushup(int rt){ int mid=LSIDE(rt)+RSIDE(rt)>>1; WHITE(rt)=WHITE(LSON)+WHITE(RSON); BLACK(rt)=BLACK(LSON)+BLACK(RSON); DATA(rt).init(); for(int i=1;i<=(n<<1);i++){ DATA(rt).uniun(i,DATA(LSON).find(i)); DATA(rt).uniun(i+(n<<1),DATA(RSON).find(i)+(n<<1)); } for(int i=1;i<=n;i++) if(chess[mid][i]==chess[mid+1][i]){ if(DATA(rt).check(i+n,i+(n<<1)))continue; DATA(rt).uniun(i+n,i+(n<<1)); WHITE(rt)-=chess[mid][i]^1; BLACK(rt)-=chess[mid][i]; } for(int i=1;i<=(n<<2);i++){ DATA(rt).find(i); map[i]=0; } for(int i=1;i<=n;i++){ if(!map[DATA(rt).father[i]]){ map[DATA(rt).father[i]]=i; DATA(rt).father[i]=i; } else DATA(rt).father[i]=map[DATA(rt).father[i]]; } for(int i=n*3+1;i<=(n<<2);i++){ if(!map[DATA(rt).father[i]]){ map[DATA(rt).father[i]]=i-(n<<1); DATA(rt).father[i]=i-(n<<1); } else DATA(rt).father[i]=map[DATA(rt).father[i]]; } for(int i=1;i<=n;i++)DATA(rt).father[i+n]=DATA(rt).father[i+n*3]; for(int i=(n<<1)+1;i<=(n<<2);i++)DATA(rt).father[i]=i; } void buildtree(int l,int r,int rt){ LSIDE(rt)=l;RSIDE(rt)=r; if(l==r){ WHITE(rt)=chess[l][1]^1; BLACK(rt)=chess[l][1]; DATA(rt).init(); DATA(rt).uniun(1+n,1); for(int i=2;i<=n;i++){ DATA(rt).uniun(i+n,i); if(chess[l][i-1]==chess[l][i])DATA(rt).uniun(i,i-1); else{ WHITE(rt)+=chess[l][i]^1; BLACK(rt)+=chess[l][i]; } } return; } int mid=l+r>>1; buildtree(l,mid,LSON); buildtree(mid+1,r,RSON); pushup(rt); } void update(int k,int rt){ if(LSIDE(rt)==RSIDE(rt)){ WHITE(rt)=chess[k][1]^1; BLACK(rt)=chess[k][1]; DATA(rt).init(); DATA(rt).uniun(1+n,1); for(int i=2;i<=n;i++){ DATA(rt).uniun(i+n,i); if(chess[k][i-1]==chess[k][i])DATA(rt).uniun(i,i-1); else{ WHITE(rt)+=chess[k][i]^1; BLACK(rt)+=chess[k][i]; } } return; } int mid=LSIDE(rt)+RSIDE(rt)>>1; if(k<=mid)update(k,LSON); else update(k,RSON); pushup(rt); } int main(){ int x,y; n=read(); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) chess[i][j]=read(); buildtree(1,n,1); m=read(); while(m--){ x=read();y=read(); chess[x][y]^=1; update(x,1); printf("%d %d ",BLACK(1),WHITE(1)); } return 0; }
然后当然是毒瘤的离线$LCT$辣!
我们可以将每个格子看做一个点, 与旁边格子有四条边相连。
处理的过程中显然会遇到环的情况, 怎么处理呢?
同样, 我们维护有关删除时间的最大生成树, 在加入边的时候判断是否为环。
如果成为环, 则与原路径中删除时间最早的边比较, 若新加入边的删除时间更晚则删除之前的那条边。
至于更新色块的个数, 我们考虑格子四个方向的连边:
如果原来颜色相同则删边, 颜色不同则连边。
因为我们保证没有环且保证路径上点删除时间尽量大, 所以我们可以用LCT维护联通信息, 只需判断是否在一棵树内即可。
不过此题离线实在恶心。。。
想想我们需要离线什么?
首先, 我们要预处理一开始边的连通性及初始答案。
其次, 我们要离线所有的修改操作, 分为四个方向考虑。
然后我们还要处理每条边删除的时间, 并以此为权值在$LCT$中连边。
注意还会有多次加入的情况。。。
然后就是一大堆细节问题了。。。
本蒟蒻已经将毁天灭地的所有调试代码删去了,可放心食用。
$namespace$封装看不习惯不要打我。。。
附代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #define MAXN 40010 #define MAXM 210 #define MAX 2147483646 using namespace std; const int fx[4]={1,-1,0,0},fy[4]={0,0,1,-1}; int n,m; int ans[2],chess[MAXM][MAXM],backup[MAXM][MAXM]; struct Question{ int x,y; }que[MAXN]; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } namespace MAP{ int num=0; int cut[MAXN<<3]; struct Edge{ int x,y,start,end,id,colour; bool flag; friend bool operator <(const Edge &p,const Edge &q){ if(p.start==q.start)return p.flag<q.flag; return p.start<q.start; } }a[MAXN<<3]; inline int point_id(int x,int y){return (x-1)*n+y;} inline int edge_id(int x1,int y1,int x2,int y2){ if(x1==x2){ if(y1>y2)swap(y1,y2); return (x1-1)*(n-1)+y1; } else{ if(x1>x2)swap(x1,x2); return n*(n-1)+(x1-1)*n+y1; } } inline void add_edge(int x,int y,int start,int id,int colour,bool flag){ num++; a[num].x=x;a[num].y=y;a[num].start=start;a[num].id=id;a[num].colour=colour; a[num].flag=flag; } inline bool check(int x,int y){ if(x<1||x>n||y<1||y>n)return true; return false; } inline void build(){ sort(a+1,a+num+1); memset(cut,-1,sizeof(cut)); } } namespace LCT{ int top=0,stack[MAXN<<4],left[MAXN<<3],right[MAXN<<3]; struct Link_Cut_Tree{ int f,flag,son[2]; int val,minn,pos; bool cut; }a[MAXN<<4]; inline bool isroot(int rt){ return a[a[rt].f].son[0]!=rt&&a[a[rt].f].son[1]!=rt; } inline void clean(int rt){ a[rt].son[0]=a[rt].son[1]=a[rt].f=a[rt].flag=a[rt].pos=a[rt].cut=0; a[rt].val=a[rt].minn=MAX; } inline void pushup(int rt){ if(!rt)return; a[rt].minn=a[rt].val;a[rt].pos=rt; if(a[rt].son[0]&&a[a[rt].son[0]].minn<a[rt].minn){a[rt].minn=a[a[rt].son[0]].minn;a[rt].pos=a[a[rt].son[0]].pos;} if(a[rt].son[1]&&a[a[rt].son[1]].minn<a[rt].minn){a[rt].minn=a[a[rt].son[1]].minn;a[rt].pos=a[a[rt].son[1]].pos;} } inline void pushdown(int rt){ if(!rt||!a[rt].flag)return; a[a[rt].son[0]].flag^=1;a[a[rt].son[1]].flag^=1;a[rt].flag^=1; swap(a[rt].son[0],a[rt].son[1]); } inline void turn(int rt){ int x=a[rt].f,y=a[x].f,k=a[x].son[0]==rt?1:0; if(!isroot(x)){ if(a[y].son[0]==x)a[y].son[0]=rt; else a[y].son[1]=rt; } a[rt].f=y;a[x].f=rt;a[a[rt].son[k]].f=x; a[x].son[k^1]=a[rt].son[k];a[rt].son[k]=x; pushup(x);pushup(rt); } void splay(int rt){ top=0; stack[++top]=rt; for(int i=rt;!isroot(i);i=a[i].f)stack[++top]=a[i].f; while(top)pushdown(stack[top--]); while(!isroot(rt)){ int x=a[rt].f,y=a[x].f; if(!isroot(x)){ if((a[y].son[0]==x)^(a[x].son[0]==rt))turn(rt); else turn(x); } turn(rt); } } inline void access(int rt){ for(int i=0;rt;i=rt,rt=a[rt].f){ splay(rt); a[rt].son[1]=i; pushup(rt); } } inline void makeroot(int rt){access(rt);splay(rt);a[rt].flag^=1;} int findroot(int rt){ access(rt);splay(rt); while(a[rt].son[0])rt=a[rt].son[0]; return rt; } inline void split(int x,int y){makeroot(x);access(y);splay(y);} inline void link(int x,int y){makeroot(x);a[x].f=y;} inline void cut(int x,int y){ split(x,y); if(a[y].son[0]==x&&a[x].f==y&&!a[x].son[1])a[x].f=a[y].son[0]=0; } inline void add_edge(int rt,int colour){ int x=MAP::a[rt].x,y=MAP::a[rt].y,end=MAP::a[rt].end,id=MAP::a[rt].id; if(findroot(y)==findroot(x)){ split(x,y); if(a[y].minn>end)return; int pos=a[y].pos; cut(pos,left[pos]);cut(pos,right[pos]); clean(pos); } else ans[colour]--; id+=MAXN-10; a[id].val=end;a[id].cut=true;a[id].pos=id; left[id]=x;right[id]=y; link(id,x);link(id,y); pushup(id); } inline void del_edge(int rt){ int x=MAP::a[rt].x,y=MAP::a[rt].y,id=MAP::a[rt].id; id+=MAXN-10; cut(id,x);cut(id,y); a[id].cut=false; } } void work(){ int now; for(now=1;now<=MAP::num&&!MAP::a[now].start;now++)LCT::add_edge(now,MAP::a[now].colour); for(int i=1;i<=m;i++){ int x=chess[que[i].x][que[i].y]; for(;now<=MAP::num&&MAP::a[now].start<=i;now++){ if(!MAP::a[now].flag){ if(!LCT::a[MAP::a[now].id+MAXN-10].cut)continue; LCT::del_edge(now); ans[x]++; } else LCT::add_edge(now,x^1); } ans[x]--;ans[x^1]++; chess[que[i].x][que[i].y]^=1; printf("%d %d ",ans[1],ans[0]); } } void init(){ int x,y,u,v,id; n=read(); ans[0]=ans[1]=0; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){ chess[i][j]=backup[i][j]=read(); ans[chess[i][j]]++; } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){ x=MAP::point_id(i,j); if(j!=n&&chess[i][j]==chess[i][j+1])MAP::add_edge(x,x+1,0,MAP::edge_id(i,j,i,j+1),chess[i][j],true); if(i!=n&&chess[i][j]==chess[i+1][j])MAP::add_edge(x,x+n,0,MAP::edge_id(i,j,i+1,j),chess[i][j],true); } m=read(); for(int i=1;i<=m;i++){ que[i].x=read();que[i].y=read(); x=MAP::point_id(que[i].x,que[i].y); for(int k=0;k<4;k++){ u=que[i].x+fx[k];v=que[i].y+fy[k];id=MAP::point_id(u,v); if(MAP::check(u,v))continue; if(backup[que[i].x][que[i].y]==backup[u][v])MAP::add_edge(x,id,i,MAP::edge_id(que[i].x,que[i].y,u,v),0,false); else MAP::add_edge(x,id,i,MAP::edge_id(que[i].x,que[i].y,u,v),0,true); } backup[que[i].x][que[i].y]^=1; } MAP::build(); for(int i=1;i<=MAP::num;i++)MAP::a[i].end=m+1; for(int i=MAP::num;i>=1;i--){ if(MAP::cut[MAP::a[i].id]!=-1)MAP::a[i].end=MAP::cut[MAP::a[i].id]; MAP::cut[MAP::a[i].id]=MAP::a[i].start; } for(int i=1;i<=MAXN-10;i++)LCT::a[i].val=MAX; } int main(){ init(); work(); return 0; }