[Wc]Dface双面棋盘()
题解:
一道维护奇怪信息的线段树。。。
我刚开始看了标签想的是删去图上一个点后求连通性
发现不会
于是退化成一般图支持删除 插入 维护连通性
发现有2两种做法
1.lct维护
按照结束顺序先后排序,给每条边一个权值
然后我们只要维护最大生成树就好了,因为这样可以保证删除当前树上的边是不会被权值更小的边替换的
而由于最大生成树的性质,是不可能能替换成更大的边的
so这说明删除它之后就不需要连边了
nlogn^2但是常数大吧
2.线段树分治
这个应该很明显吧,变成只有插入的并查集问题
nlogn^2logn^2 本来常数不大但是自带了4
写代码:1h
debug:30min
#pragma G++ optimize (2) #include <bits/stdc++.h> using namespace std; #define IL inline #define rint register int char ss[1<<24],*A=ss,*B=ss; IL char gc(){return A==B&&(B=(A=ss)+fread(ss,1,1<<24,stdin),A==B)?EOF:*A++;} template<class T>void read(T&x){ rint f=1,c;while(c=gc(),c<48||57<c)if(c=='-')f=-1;x=c^48; while(c=gc(),47<c&&c<58)x=(x<<3)+(x<<1)+(c^48);x*=f; } char sr[1<<24],z[20];int C=-1,Z; template<class T>void wer(T x){ if(x<0)sr[++C]='-',x=-x; while(z[++Z]=x%10+48,x/=10); while(sr[++C]=z[Z],--Z);sr[++C]=' '; } const int N=210; const int N2=1e4+1e3; int a[N][N],f1[N][N][5],f0[N][N][5],n,m; int dx[5]={0,1,-1,0,0}; int dy[5]={0,0,0,1,-1}; int pos[5]={0,2,1,4,3}; IL bool pd(int x,int y) { if (1<=x&&x<=n&&1<=y&&y<=n) return(1); else return(0); } struct re{ int a,b,c; }; IL int js(int x,int y) { return(x*n+y); } struct sgt { vector<re> ve[N2*4]; int fa[N*N],count2[N*N],ans[N2]; sgt() { for (int i=1;i<=N*N-1;i++) fa[i]=i,count2[i]=1;} #define mid ((h+t)>>1) void insert(int x,int h,int t,int h1,int t1,re k) { if (h1>t1) return; if (h1<=h&&t<=t1) { ve[x].push_back(k); return; } if (h1<=mid) insert(x*2,h,mid,h1,t1,k); if (mid<t1) insert(x*2+1,mid+1,t,h1,t1,k); } IL int find(int &x) { while (fa[x]!=x) x=fa[x]; } void dfs(int x,int h,int t,int cnt) { // cout<<x<<" "<<h<<" "<<t<<endl; stack<re> s; int cnt2=cnt; for (rint i=0;i<ve[x].size();i++) { re t=ve[x][i]; rint x1=js(t.a,t.b),x2=js(t.a+dx[t.c],t.b+dy[t.c]); find(x1); find(x2); if (x1!=x2) { if (count2[x1]>count2[x2]) swap(x1,x2); cnt2++; s.push((re){x1,x2,count2[x2]}); fa[x1]=x2; count2[x2]+=count2[x1]; } } if (h==t) ans[h]=cnt2; else { dfs(x*2,h,mid,cnt2); dfs(x*2+1,mid+1,t,cnt2); } while (!s.empty()) { re t=s.top(); s.pop(); fa[t.a]=t.a; count2[t.b]=t.c; } } }se1,se0; int main() { freopen("1.in","r",stdin); freopen("1.out","w",stdout); read(n); int ans[2][N2]={}; for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) { read(a[i][j]); ans[a[i][j]][0]++; } for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) for(int k=1;k<=4;k++) f1[i][j][k]=-1,f0[i][j][k]=-1; for(int i=1;i<=n;i++) for (int j=1;j<=n;j++) for(int k=1;k<=4;k++) if (pd(i+dx[k],j+dy[k])&&a[i][j]==a[i+dx[k]][j+dy[k]]) if (a[i][j]) f1[i][j][k]=0; else f0[i][j][k]=0; read(m); for (int i=1;i<=m;i++) { int x,y; read(x); read(y); for (int k=1;k<=4;k++) if (pd(x+dx[k],y+dy[k])) if (a[x][y]==1) { if (a[x+dx[k]][y+dy[k]]==a[x][y]) se1.insert(1,1,m,max(f1[x][y][k],1),i-1,(re){x,y,k}), f1[x][y][k]=-1,f1[x+dx[k]][y+dy[k]][pos[k]]=-1; else f0[x][y][k]=i,f0[x+dx[k]][y+dy[k]][pos[k]]=i; } else if (a[x+dx[k]][y+dy[k]]==a[x][y]) se0.insert(1,1,m,max(f0[x][y][k],1),i-1,(re){x,y,k}), f0[x][y][k]=-1,f0[x+dx[k]][y+dy[k]][pos[k]]=-1; else f1[x][y][k]=i,f1[x+dx[k]][y+dy[k]][pos[k]]=i; if(a[x][y]==0) ans[1][i]=ans[1][i-1]+1,ans[0][i]=ans[0][i-1]-1; else ans[0][i]=ans[0][i-1]+1,ans[1][i]=ans[1][i-1]-1; a[x][y]^=1; } for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) for (int k=1;k<=4;k++) { if (f1[i][j][k]>=0) { se1.insert(1,1,m,max(1,f1[i][j][k]),m,(re){i,j,k}); } if (f0[i][j][k]>=0) { se0.insert(1,1,m,max(1,f0[i][j][k]),m,(re){i,j,k}); } } /* for (int i=1;i<=1;i++) for (int j=0;j<se1.ve[i].size();j++) cout<<se1.ve[1][j].a<<" "<<se1.ve[1][j].b<<" "<<se1.ve[1][j].c<<endl; cout<<endl; */ se1.dfs(1,1,m,0); se0.dfs(1,1,m,0); for(int i=1;i<=m;i++) wer(ans[1][i]-se1.ans[i]),wer(ans[0][i]-se0.ans[i]),sr[++C]='\n'; fwrite(sr,1,C+1,stdout); // changshi fen ge fu return 0; }
然后由于这个牵扯出loj122这题 维护动态图连通性
并没有看懂网上的唯一一篇题解于是弃疗
正解:
线段树上的每个叶子节点表示一行
每个节点维护当前范围内的黑白区间个数
合并的时候就用并查集启发式合并就可以
每次合并是o(n)的
每次修改进行log次
所以复杂度应该是O(nmlogn)
相比上面两种都不优吧 但是常数小了至少2倍 因为这个是单点修改上面的要修改4条边
写完我发现我这种写法极其冗长
由于0,1完全等价
完全可以把它们弄成结构题然后修改做两次 这样代码就可以短大约一半低于普遍长度了
写代码:1h+
调试:40min
数据结构的代码能力很需要提升啊
如果这题能做到30min写完20min调完我觉得那就能很强了
不过这个时间我觉得写得常数可能非常大
经过优化的线段树分治只用了0.3s不到(本地)
这个用了2.4s
主要消耗时间的无疑是updata这里
我写得时候想的是先将两个儿子合并在一起,然后再搞
然后我就合并了4*n
其实只要中间的2*n就可以了,这样常数是2
另外我刚开始统计联通块数目用的是一个很傻比的方法
里面的一些信息没删可能也导致了时间的增加
于是我决定再去卡一波常数
这个是源代码:
#pragma G++ optimize (2) #include <bits/stdc++.h> using namespace std; #define IL inline #define rint register int char ss[1<<24],*A=ss,*B=ss; IL char gc(){return A==B&&(B=(A=ss)+fread(ss,1,1<<24,stdin),A==B)?EOF:*A++;} template<class T>void read(T&x){ rint f=1,c;while(c=gc(),c<48||57<c)if(c=='-')f=-1;x=c^48; while(c=gc(),47<c&&c<58)x=(x<<3)+(x<<1)+(c^48);x*=f; } char sr[1<<24],z[20];int C=-1,Z; template<class T>void wer(T x){ if(x<0)sr[++C]='-',x=-x; while(z[++Z]=x%10+48,x/=10); while(sr[++C]=z[Z],--Z);sr[++C]=' '; } const int N=210; const int N2=1e4+1e3; int a[N][N],n,m,ph[N*4],pt[N*4]; struct bcj{ int fa[N*4][N*2],data[N*4]; int find(int x,int y) { int ans; if (fa[x][y]!=y) ans=find(x,fa[x][y]); else return(y); fa[x][y]=ans; return(ans); } }b1,b2; struct mn{ int fa[N*4],pos[N*4]; bool f[N*4]; int find(int x) { int ans; if (fa[x]!=x) ans=find(fa[x]); else return(x); fa[x]=ans; return(ans); } }c1,c2; IL bool pd(int x) { if ((1<=x&&x<=n)||(3*n<x&&x<=4*n)) return(1); else return(0); } IL int mex(int x,int y) { if (y==0) return(0); else return(x+y); } void updata(int x) { int h1=ph[x*2],t1=pt[x*2],h2=ph[x*2+1],t2=pt[x*2+1]; for (int i=1;i<=2*n;i++) c1.fa[i]=b1.fa[x*2][i],c2.fa[i]=b2.fa[x*2][i]; for (int i=1;i<=2*n;i++) c1.fa[i+2*n]=mex(2*n,b1.fa[x*2+1][i]), c2.fa[i+2*n]=mex(2*n,b2.fa[x*2+1][i]); int cnt1=0,cnt2=0; for (int i=1;i<=n;i++) if (a[t1][i]==a[h2][i]) if (a[t1][i]) { int x1=c1.find(n+i); int x2=c1.find(2*n+i); if (x1!=x2&&x1!=0&&x2!=0) c1.fa[x1]=x2,cnt1++; } else { int x1=c2.find(n+i); int x2=c2.find(2*n+i); if (x1!=x2&&x1!=0&&x2!=0) c2.fa[x1]=x2,cnt2++; } for (int i=1;i<=4*n;i++) c1.f[i]=c2.f[i]=c1.pos[i]=c2.pos[i]=0; c1.f[0]=c2.f[0]=1; for (int i=1;i<=n;i++) { int x1=c1.find(i); if (x1) if (pd(x1)) { b1.fa[x][i]=x1; if(!c1.f[x1]) c1.f[x1]=1; } else if (c1.pos[x1]) b1.fa[x][i]=c1.pos[x1]; else b1.fa[x][i]=i,c1.pos[x1]=i; int x2=c2.find(i); if (x2) if (pd(x2)) { b2.fa[x][i]=x2; if(!c2.f[x2]) c2.f[x2]=1; } else if (c2.pos[x2]) b2.fa[x][i]=c2.pos[x2]; else b2.fa[x][i]=i,c2.pos[x2]=i; } for(int i=3*n+1;i<=4*n;i++) { int x1=c1.find(i); if (x1) if (pd(x1)) { b1.fa[x][i-2*n]=x1; if(!c1.f[x1]) c1.f[x1]=1; } else if (c1.pos[x1]) b1.fa[x][i-2*n]=c1.pos[x1]; else b1.fa[x][i-2*n]=i,c1.pos[x1]=i; int x2=c2.find(i); if (x2) if (pd(x2)) { b2.fa[x][i-2*n]=x2; if(!c2.f[x2]) c2.f[x2]=1; } else if (c2.pos[x2]) b2.fa[x][i-2*n]=c2.pos[x2]; else b2.fa[x][i-2*n]=i,c2.pos[x2]=i; } b1.data[x]=b1.data[x*2]+b1.data[x*2+1]-cnt1; b2.data[x]=b2.data[x*2]+b2.data[x*2+1]-cnt2; for (int i=1;i<=2*n;i++) { if (b1.fa[x][i]>2*n) b1.fa[x][i]-=2*n; if (b2.fa[x][i]>2*n) b2.fa[x][i]-=2*n; } } #define mid ((h+t)/2) void build(int x,int h,int t) { ph[x]=h; pt[x]=t; if (h==t) { int cnt1=0,cnt2=0; for (int i=1;i<=n;i++) if (a[h][i]) b1.fa[x][i]=i,b2.fa[x][i]=0,cnt1++; else b1.fa[x][i]=0,b2.fa[x][i]=i,cnt2++; for (int i=n+1;i<=2*n;i++) b1.fa[x][i]=b1.fa[x][i-n],b2.fa[x][i]=b2.fa[x][i-n]; for (int i=1;i<=n-1;i++) if (a[h][i]==a[h][i+1]) if (a[h][i]) { int x1=b1.find(x,i); int x2=b1.find(x,i+1); if (x1!=x2) b1.fa[x][x1]=x2,cnt1--; } else { int x1=b2.find(x,i); int x2=b2.find(x,i+1); if (x1!=x2) b2.fa[x][x1]=x2,cnt2--; } b1.data[x]=cnt1; b2.data[x]=cnt2; return; } build(x*2,h,mid); build(x*2+1,mid+1,t); updata(x); } void change(int x,int h,int t,int pos,int k) { if (h==t) { a[pos][k]^=1; int cnt1=0,cnt2=0; for (int i=1;i<=n;i++) if (a[h][i]) b1.fa[x][i]=i,b2.fa[x][i]=0,cnt1++; else b1.fa[x][i]=0,b2.fa[x][i]=i,cnt2++; for (int i=n+1;i<=2*n;i++) b1.fa[x][i]=b1.fa[x][i-n],b2.fa[x][i]=b2.fa[x][i-n]; for (int i=1;i<=n-1;i++) if (a[h][i]==a[h][i+1]) if (a[h][i]) { int x1=b1.find(x,i); int x2=b1.find(x,i+1); if (x1!=x2) b1.fa[x][x1]=x2,cnt1--; } else { int x1=b2.find(x,i); int x2=b2.find(x,i+1); if (x1!=x2) b2.fa[x][x1]=x2,cnt2--; } b1.data[x]=cnt1; b2.data[x]=cnt2; return; } if (pos<=mid) change(x*2,h,mid,pos,k); else change(x*2+1,mid+1,t,pos,k); updata(x); } int main() { freopen("1.in","r",stdin); freopen("1.out","w",stdout); read(n); for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) read(a[i][j]); build(1,1,n); // cout<<b1.data[1]<<" "<<b2.data[1]<<endl; read(m); for (int i=1;i<=m;i++) { int x,y; read(x); read(y); change(1,1,n,x,y); wer(b1.data[1]); wer(b2.data[1]); sr[++C]='\n'; } fwrite(sr,1,C+1,stdout); return 0; }