【CodeChef】Chef and Graph Queries
阿新 • • 發佈:2018-10-05
比較 spl roo font 聯通塊 解決 計算 string ger
條邊的生成樹中,每個編號的邊能被多少條編號更大的邊替換掉,為了方便查詢,那些不需要替換直接加入的邊統一加到\(0\)的位置,然後查詢的時候只要在第\(r\)棵和第\(l-1\)棵中查一下\([0,l-1]\)的和然後相減一下,再拿\(n\)減一下就是答案了
Portal --> CC Chef and Graph Queries
Solution
快樂數據結構題(然而好像有十分優秀的莫隊+可撤銷並查集搞法qwq)
首先考慮一種方式來方便一點地。。計算一個圖的聯通塊數量:我們可以考慮容斥,維護每個連通塊的生成樹,然後\(n-\)生成樹邊數就是答案了
這樣有一個好,加邊的時候比較好處理,但是光這樣並不能解決我們的問題
? 順著這個思路思考,先不考慮時間復雜度,對於一個詢問,考慮將編號為\(l\sim r\)的邊一條一條加入第\(1\sim l-1\)條邊得到的生成樹(們)中(先不考慮這個生成樹邊的選擇方式),考慮一條邊有貢獻(成為新的生成樹(們)中的一部分)的情況:
(1)這條邊可以替換掉\(1\sim l-1\)中的某條邊
(2)這條邊的兩個端點當前不連通
? 所以問題就變成了,看\(l\sim r\)中有多少條邊可以替換掉在生成樹中的編號在\(1\sim l-1\)範圍內的邊再加上(2)情況中的邊
這個時候,我們就可以確定生成樹邊的選擇方式了:因為要讓能替換掉在\(1\sim l-1\)範圍內的邊盡量多,所以一旦當前邊可以替換掉另一條邊,我們肯定優先選擇編號小的替換
再註意到在考慮詢問\((l,r)\)的時候,我們其實相當於要得到\(1\sim r\)的生成樹(們),於是我們就可以預處理,按順序加邊,用LCT維護當前的生成樹(們),再用一棵主席樹(按權值存)維護一下第\(1\sim i\)
最後還有一點就是。。因為要支持刪邊操作,所以LCT裏面把邊也看成一個點就好啦ovo
代碼大概長這個樣子
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N=2*(1e5)+10,SEG=N*20,inf=2147483647; int rec[N][2]; int n,m,Q,T; namespace Lct{/*{{{*/ const int N=::N*2; int ch[N][2],mn[N],fa[N],rev[N],val[N]; void reset(int x){ ch[x][0]=ch[x][1]=0; fa[x]=0; val[x]=mn[x]=inf; } void clear(int n){ for (int i=1;i<=n;++i) reset(i); } bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;} int which(int x){return ch[fa[x]][1]==x;} void reverse(int x){ swap(ch[x][0],ch[x][1]); rev[x]^=1; } void pushdown1(int x){ if (!rev[x]) return; if (ch[x][0]) reverse(ch[x][0]); if (ch[x][1]) reverse(ch[x][1]); rev[x]=0; } void pushdown(int x){ if (!isroot(x)) pushdown(fa[x]); pushdown1(x); } void pushup(int x){ mn[x]=val[x]; if (ch[x][0]) mn[x]=min(mn[x],mn[ch[x][0]]); if (ch[x][1]) mn[x]=min(mn[x],mn[ch[x][1]]); } void rotate(int x){ int dir=which(x),f=fa[x]; if (!isroot(f)) ch[fa[f]][which(f)]=x; fa[x]=fa[f]; fa[f]=x; if (ch[x][dir^1]) fa[ch[x][dir^1]]=f; ch[f][dir]=ch[x][dir^1]; ch[x][dir^1]=f; pushup(f); pushup(x); } void splay(int x){ pushdown(x); for (int f=fa[x];!isroot(x);f=fa[x]){ if (!isroot(f)) rotate(which(f)==which(x)?f:x); rotate(x); } pushup(x); } void access(int x){ for (int last=0;x;last=x,x=fa[x]){ splay(x); ch[x][1]=last; pushup(x); } } void make_rt(int x){ access(x); splay(x); reverse(x); } bool connected(int x,int y){ if (x==y) return true; make_rt(x); access(y); splay(y); return fa[x]; } void link(int x,int y){ make_rt(x); fa[x]=y; access(x); splay(x); } void cut(int x,int y){ make_rt(x); access(y); splay(y); fa[x]=0; ch[y][0]=0; pushup(y); } int query(int x,int y){ make_rt(x); access(y); splay(y); return mn[y]; } }/*}}}*/ namespace Seg{/*{{{*/ int ch[SEG][2],sum[SEG],rt[SEG]; int n,tot; void clear(){ for (int i=0;i<=tot;++i) ch[i][0]=ch[i][1]=0,sum[i]=0; tot=0; } void init(int _n){clear();n=_n;} int newnode(int pre){ ch[++tot][0]=ch[pre][0]; ch[tot][1]=ch[pre][1]; sum[tot]=sum[pre]; return tot; } void _insert(int pre,int &x,int d,int lx,int rx){ x=newnode(pre); ++sum[x]; if (lx==rx) return; int mid=lx+rx>>1; if (d<=mid) _insert(ch[pre][0],ch[x][0],d,lx,mid); else _insert(ch[pre][1],ch[x][1],d,mid+1,rx); } void insert(int pre,int x,int d){_insert(rt[pre],rt[x],d,1,n);} int _query(int L,int R,int l,int r,int lx,int rx){ if (!L&&!R) return 0; if (l<=lx&&rx<=r) return sum[R]-sum[L]; int mid=lx+rx>>1,ret=0; if (l<=mid) ret+=_query(ch[L][0],ch[R][0],l,r,lx,mid); if (r>mid) ret+=_query(ch[L][1],ch[R][1],l,r,mid+1,rx); return ret; } int query(int L,int R,int l,int r){return _query(rt[L-1],rt[R],l,r,1,n);} }/*}}}*/ void init(){ Lct::clear(n+m); Seg::init(m+1); } void debug(int x){ printf("#%d:\n",x); for (int i=0;i<=m;++i) printf("%d ",Seg::query(x-1,x,i+1,i+1)); printf("\n"); } void solve(){ int x,y,tmp; for (int i=1;i<=m;++i){ scanf("%d%d",&rec[i][0],&rec[i][1]); x=rec[i][0]; y=rec[i][1]; if (x==y){ Seg::rt[i]=Seg::rt[i-1]; continue; } Lct::val[n+i]=i; if (Lct::connected(x,y)){ tmp=Lct::query(x,y); Lct::cut(rec[i][0],n+tmp); Lct::cut(rec[i][1],n+tmp); Lct::link(x,n+i); Lct::link(y,n+i); Seg::insert(i-1,i,tmp+1); } else{ Lct::link(x,n+i); Lct::link(y,n+i); Seg::insert(i-1,i,0+1); } //debug(i); } int l,r; for (int i=1;i<=Q;++i){ scanf("%d%d",&l,&r); printf("%d\n",n-Seg::query(l,r,0+1,(l-1)+1)); } } int main(){ #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); #endif scanf("%d",&T); for (int o=1;o<=T;++o){ scanf("%d%d%d",&n,&m,&Q); init(); solve(); } }
【CodeChef】Chef and Graph Queries