標籤:php down 最小 基礎 rev algorithm out pair 答案
題面
bzoj上的強制線上版本
思路
首先可以確定,這類聯通塊相關的詢問問題,都可以$LCT$+可持久化記錄解決
用LCT維護產生樹作為演算法基礎
具體而言,從前往後按照邊的編號順序掃一遍邊
如果這條邊兩端不在同一個$LCT$聯通塊中,則$link$
否則$cut$掉當前串連兩條邊的路徑上的編號最小的邊,並$link$
記錄$ntr[i]$表示第$i$條邊觸發第二種情況時$link$前$cut$掉的邊的編號
如果觸發第一種情況,則$ntr[i]=0$
如果為自環,則$ntr[i]=i$
這樣記錄之後,建立$ntr[i]$的主席樹,每次在$ntr[i]$的位置+1
對於查詢$[l,r]$,$Ans=n-query(0,l-1,root[l-1],root[r])$
具體原因為:考慮每個$ntr$值大於等於$l$的邊,顯然它們在不在對答案都沒有貢獻(因為已經被ntr了)
否則,它們會串連兩個聯通塊,但是這條邊不在$[l,r]$區間內,所以有聯通塊個數-1的貢獻
Code
#include<iostream>#include<cstring>#include<algorithm>#include<cstdio>#define mp make_pairusing namespace std;inline int read(){ int re=0,flag=1;char ch=getchar(); while(!isdigit(ch)){ if(ch=='-') flag=-1; ch=getchar(); } while(isdigit(ch)) re=(re<<1)+(re<<3)+ch-'0',ch=getchar(); return re*flag;}int fa[400010],ch[400010][2],val[400010],minn[400010],rev[400010];void update(int x){ minn[x]=x; if(val[minn[x]]>val[minn[ch[x][0]]]) minn[x]=minn[ch[x][0]]; if(val[minn[x]]>val[minn[ch[x][1]]]) minn[x]=minn[ch[x][1]];}int nroot(int x){return ((ch[fa[x]][0]==x)||(ch[fa[x]][1]==x));}int get(int x){return ch[fa[x]][1]==x;}void pushrev(int x){ if(!x) return; swap(ch[x][0],ch[x][1]); rev[x]^=1;}void pushdown(int x){ if(!rev[x]) return; pushrev(ch[x][0]); pushrev(ch[x][1]); rev[x]=0;}void push(int x){ if(!x) return; if(nroot(x)) push(fa[x]); pushdown(x);}void rotate(int x){ int f=fa[x],ff=fa[f],son=get(x),nr=nroot(f);// cout<<" rotate "<<x<<' '<<f<<' '<<ff<<' '<<son<<' '<<ch[x][0]<<' '<<ch[x][1]<<' '<<ch[f][0]<<' '<<ch[f][1]<<'\n'; ch[f][son]=ch[x][son^1]; if(ch[f][son]) fa[ch[f][son]]=f; fa[f]=x;ch[x][son^1]=f; fa[x]=ff; if(nr) ch[ff][ch[ff][1]==f]=x; update(f);update(x);}void splay(int x){// cout<<" splay "<<x<<'\n'; push(x); for(int f;nroot(x);rotate(x)){ f=fa[x]; if(nroot(f)) rotate((get(x)==get(f))?f:x); }}void access(int x){// cout<<"access "<<x<<'\n'; for(int y=0;x;y=x,x=fa[x]){ splay(x);ch[x][1]=y;update(x);// cout<<" do "<<x<<' '<<fa[x]<<' '<<y<<'\n'; }}void mroot(int x){ access(x);splay(x);pushrev(x);}void link(int u,int v){ mroot(u);fa[u]=v;}void cut(int u,int v){ mroot(u);access(v);splay(v); fa[u]=ch[v][0]=0;}int find(int u){ access(u);splay(u); while(ch[u][0]) u=ch[u][0]; return u;}int query(int u,int v){ mroot(u);access(v);splay(v); return minn[v];}int lc[4000010],rc[4000010],seg[4000010],cnt;int ntr[400010],root[400010];int insert(int l,int r,int pre,int pos){ int cur=++cnt,mid=(l+r)>>1; lc[cur]=lc[pre];rc[cur]=rc[pre];seg[cur]=seg[pre]+1; if(l==r) return cur; if(mid>=pos) lc[cur]=insert(l,mid,lc[pre],pos); else rc[cur]=insert(mid+1,r,rc[pre],pos); return cur;}int query(int l,int r,int ql,int qr,int pre,int cur){ if(l>=ql&&r<=qr) return seg[cur]-seg[pre]; int mid=(l+r)>>1,re=0; if(mid>=ql) re+=query(l,mid,ql,qr,lc[pre],lc[cur]); if(mid<qr) re+=query(mid+1,r,ql,qr,rc[pre],rc[cur]); return re;}int n,m,q;void init(){ n=read();m=read();q=read();cnt=0; memset(lc,0,sizeof(lc));memset(rc,0,sizeof(rc));memset(seg,0,sizeof(seg)); for(int i=1;i<=n;i++) fa[i]=ch[i][0]=ch[i][1]=0,minn[i]=i,val[i]=1e9,rev[i]=0; val[0]=1e9;}pair<int,int>e[200010];int main(){ int T=read(),i,t1,t2,tmp,w; while(T--){ init();// cout<<n<<' '<<m<<' '<<q<<'\n'; for(i=1;i<=m;i++){ t1=read();t2=read(); e[i]=mp(t1,t2);// cout<<"input "<<t1<<' '<<t2<<'\n'; if(t1==t2){ntr[i]=i;continue;} if(find(t1)==find(t2)){ tmp=query(t1,t2);w=val[tmp]; ntr[i]=w; cut(tmp,e[w].first); cut(tmp,e[w].second); } else ntr[i]=0;// cout<<"passed "<<ntr[i]<<'\n'; fa[n+i]=ch[n+i][0]=ch[n+i][1]=0; val[n+i]=i;minn[n+i]=n+i;rev[n+i]=0; link(n+i,t1);link(n+i,t2); } for(i=1;i<=m;i++) root[i]=insert(0,m,root[i-1],ntr[i]); for(i=1;i<=q;i++){ t1=read();t2=read(); printf("%d\n",n-query(0,m,0,t1-1,root[t1-1],root[t2])); } }}
[bzoj3514][CodeChef GERALD07] Chef ans Graph Queries [LCT+主席樹]