BZOJ 3514 Codechef MARCH14 GERALD07加强版
题目链接:Codechef MARCH14 GERALD07加强版
看到这种求连边求联通块个数的题,大概思路就是计算一下有多少条边连接了两个不同的联通块,然后用总点数减一下。
然后我们该如何判断一条边是有效边呢?我们可以先用\(LCT\)来维护原图,当形成环的时候就把换上最早出现的边给弹掉。我们记\(a_i\)表示第\(i\)条边弹掉的边的编号(如果没有就为\(0\)),那么当我们只保留编号在\([l,r]\)内的边时,如果这其中的一条边\(x\)满足\(a_x<l\),那么这条边才是有用的。即这条边连接了两个联通块,会使得联通块个数\(-1\)。
于是使用\(LCT\)预处理之后用主席树维护一下\(a\)数组就做完了。
下面贴代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) #define isroot(x) (x!=s[fa[x]][0] && x!=s[fa[x]][1]) #define maxn 200010 #define MAXN maxn*20 #define INF (1<<29) using namespace std; typedef long long llg; struct data{ int u,v; }a[maxn]; int n,m,q,ty,ff[maxn],d[maxn<<1],val[maxn<<1],tt; int fa[maxn<<1],s[maxn<<1][2],minv[maxn<<1]; int rt[maxn],sumv[MAXN],le[MAXN],ri[MAXN],L,ans; bool rev[maxn<<1]; int getint(){ int w=0;bool q=0; char c=getchar(); while((c>'9'||c<'0')&&c!='-') c=getchar(); if(c=='-') c=getchar(),q=1; while(c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w; } int find(int x){return ff[ff[x]]==ff[x]?ff[x]:ff[x]=find(ff[x]);} void update(int u){ int l=s[u][0],r=s[u][1]; minv[u]=u; if(val[minv[l]]<val[minv[u]]) minv[u]=minv[l]; if(val[minv[r]]<val[minv[u]]) minv[u]=minv[r]; } void rotate(int u){ int p=fa[u],g=fa[p]; bool l=(u==s[p][1]),r=!l; if(!isroot(p)) s[g][p==s[g][1]]=u; fa[s[u][r]]=p; s[p][l]=s[u][r]; s[u][r]=p; fa[p]=u; fa[u]=g; update(p); update(u); } void splay(int u){ int ld=0; d[ld=1]=u; for(int i=u;!isroot(i);i=fa[i]) d[++ld]=fa[i]; for(int i=ld,x;x=d[i],i;i--) if(rev[x]){ rev[s[x][0]]^=1,rev[s[x][1]]^=1; swap(s[x][0],s[x][1]); rev[x]=0; } while(!isroot(u)){ int p=fa[u],g=fa[p]; if(!isroot(p)){ if((u==s[p][1])^(p==s[g][1])) rotate(u); else rotate(p); } rotate(u); } } void access(int u){for(int t=0;u;t=u,u=fa[u]) splay(u),s[u][1]=t,update(u);} void makert(int u){access(u); splay(u); rev[u]^=1;} void link(int x,int y){makert(x); fa[x]=y;} void cut(int x,int y){ makert(x); access(y),splay(y); s[y][0]=fa[x]=0; update(y); } int query1(int x,int y){ makert(x); access(y); splay(y); return minv[y]; } int add(int u,int l,int r){ int now=++tt,mid=(l+r)>>1; sumv[now]=sumv[u]+1; le[now]=le[u]; ri[now]=ri[u]; if(l!=r){ if(L<=mid) le[now]=add(le[u],l,mid); else ri[now]=add(ri[u],mid+1,r); } return now; } int query2(int u,int v){ int l=0,r=m,mid,now=0; while(l!=r){ mid=(l+r)>>1; if(L<=mid) u=le[u],v=le[v],r=mid; else now+=sumv[le[u]]-sumv[le[v]],u=ri[u],v=ri[v],l=mid+1; } return now; } int main(){ File("a"); n=getint(); m=getint(); q=getint(); ty=getint(); for(int i=1;i<=n;i++) ff[i]=i,val[i]=INF; val[0]=INF; for(int i=1,u,v,x;i<=m;i++){ a[i].u=u=getint(); a[i].v=v=getint(); x=n,val[i+n]=i; if(find(u)!=find(v)) ff[find(u)]=find(v); else if(u!=v) x=query1(u,v),cut(a[x-n].u,x),cut(a[x-n].v,x); if(u!=v) link(u,i+n),link(v,i+n); else x+=m; L=x-n,rt[i]=add(rt[i-1],0,m); } while(q--){ int l=getint(),r=getint(); if(ty) l^=ans,r^=ans; L=l; printf("%d\n",ans=n-query2(rt[r],rt[l-1])); } return 0; }
这种码农题写完就过的感觉真爽(爆数组\(RE\)的那一发不算)