1. 程式人生 > >[WC2005]雙面棋盤(並查集+分治)

[WC2005]雙面棋盤(並查集+分治)

href oot 時間 獨立 src 連通 一道 info style

題目描述

技術分享圖片

題解

唉,還是碼力不行,寫了一個多小時發現想錯了又重構了一個多小時。

這道題意圖很顯然,動態維護聯通塊,有一個經典做法就是用LCT維護按照刪除時間維護的最大生成樹。

網上還有一種神奇的做法,線段樹套並查集,蒟蒻表示不懂。。

這道題可以利用並查集操作可以撤銷這種性質來做。

線段樹分治

線段樹分治可以分兩種情況,操作之間獨立和操作之間不獨立。

操作之間獨立意味著我先完成哪個操作就可以,例如找最優點,有一道例題。

還有一種是操作之間是可以相互影響的,比如說這道題,連通性這種東西和我加的每一條邊都有關。

我們可以按時間分治,先離線找出每條邊出現的時間段,把這些時間段加入線段樹中,然後在線段樹上dfs,進入節點時把所有邊加入,刪除時棧序撤銷來的時候的操作(因為線段樹dfs的過程也是壓棧彈棧的過程,所以我們可以準確撤銷操作),然後在根節點統計答案,聯通塊的個數為點數-邊數,點數這種東西我們可以直接離線維護(我一開始傻了)。

代碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define N 40002
#define maxn 209
using namespace std;
int n,id[maxn][maxn],f[N],tot,ls[N<<1],rs[N<<1],b[N],bl,w[N],wl,deep[N],num,last[N<<1],m,root,a[maxn][maxn],bian;
int tag[maxn][maxn][4
],anti[4],color[N<<1]; const int dx[4]={0,1,-1,0}; const int dy[4]={1,0,0,-1}; inline int rd(){ int x=0;char c=getchar();bool f=0; while(!isdigit(c)){if(c==-)f=1;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();} return f?-x:x; } int find(int x){return
f[x]==x?x:find(f[x]);} struct node{int id,co;}; struct rbs{int dep,root,link,co;}; struct node2{int x,y;}; vector<node>vec[N<<1]; vector<rbs>zh[N<<1]; node2 linko[N<<1]; void upd(int &cnt,int l,int r,int L,int R,int x,int y){ if(!cnt)cnt=++tot; if(l>=L&&r<=R){vec[cnt].push_back(node{x,y});return;} int mid=(l+r)>>1; if(mid>=L)upd(ls[cnt],l,mid,L,R,x,y); if(mid<R)upd(rs[cnt],mid+1,r,L,R,x,y); } void solve(int &cnt,int l,int r){ if(!cnt)cnt=++tot; //cout<<l<<" **** "<<r<<endl; for(int i=0;i<vec[cnt].size();++i){ int id=vec[cnt][i].id,co=vec[cnt][i].co; int x=linko[id].x,y=linko[id].y; int xx=find(x),yy=find(y); if(xx!=yy){ if(co)bl++;else wl++; if(deep[xx]<deep[yy])swap(xx,yy); zh[cnt].push_back(rbs{deep[xx],xx,yy,co}); f[yy]=xx;deep[xx]=max(deep[xx],deep[yy]+1); } } // cout<<b[l]<<" "<<w[l]<<" "<<bl<<" "<<wl<<endl; if(l==r){if(l)printf("%d %d\n",b[l]-bl,w[l]-wl);} else{ int mid=(l+r)>>1; solve(ls[cnt],l,mid); solve(rs[cnt],mid+1,r); } while(zh[cnt].size()){ rbs x=zh[cnt].back();zh[cnt].pop_back(); deep[x.root]=x.dep;f[x.link]=x.link; if(x.co)bl--;else wl--; } } int main(){ anti[0]=3;anti[3]=0;anti[2]=1;anti[1]=2; n=rd();int x,y; for(int i=1;i<=n;++i) for(int j=1;j<=n;++j){ a[i][j]=rd(),id[i][j]=++num; if(a[i][j])b[0]++;else w[0]++; } for(int i=1;i<=num;++i)f[i]=i,deep[i]=1; memset(last,-1,sizeof(last)); for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) for(int k=0;k<2;++k){ int xx=i+dx[k],yy=j+dy[k]; if(!id[xx][yy])continue; tag[i][j][k]=tag[xx][yy][anti[k]]=++bian; if(a[i][j]==a[xx][yy])last[bian]=0,color[bian]=a[i][j]; linko[bian]=node2{id[i][j],id[xx][yy]}; } m=rd(); for(int i=1;i<=m;++i){ x=rd();y=rd();b[i]=b[i-1];w[i]=w[i-1]; if(a[x][y])b[i]--,w[i]++;else b[i]++,w[i]--; for(int k=0;k<4;++k){ int xx=x+dx[k],yy=y+dy[k],_id=tag[x][y][k]; if(!_id)continue; if(a[x][y]==a[xx][yy]) upd(root,0,m,last[_id],i-1,_id,a[x][y]),last[_id]=-1; else last[_id]=i,color[_id]=a[xx][yy]; } a[x][y]^=1; } for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) for(int k=0;k<2;++k){ int xx=i+dx[k],yy=j+dy[k],_id=tag[i][j][k]; if(~last[_id])upd(root,0,m,last[_id],m,_id,color[_id]); } solve(root,0,m); return 0; }

[WC2005]雙面棋盤(並查集+分治)