感觉这题是真的神,,太瓜了根本想不到。
N个点M条边的无向图,询问保留图中编号在$[l,r]$的边的时候图中的联通块个数。
考虑每条边的贡献。一条边可以使联通块数量-1当且仅当加入这条边之后,不形成环即连接了两个联通块。
后加入的边如果形成了环呢?那么只要环中最早加入的边还在,就不会做出贡献。
那么我们按编号从小到大加边,记录对于每条边,加入之后,如果形成环,形成的环中最早也就是编号最小的边是多少;否则就是0。记为$ntr[i]$。(???wtf?)
也就是说只有询问的$l>ntr[i]$时,边$i$才能做出贡献(当然也要$i \leq r$)。
$ntr[i]$可以用LCT维护。(维护边权,就是把边变成点做就行了。装逼的做法嘛,jcy大爷有写过博客)
询问区间有多少$ntr[i]<l$的可以用主席树维护。
然后我是傻逼。LCT在access时候忘了pullup。rotate时候先更新的x,再更新的原父亲。蠢死了。
#include<bits/stdc++.h> using namespace std; const int N=400010; inline int read(){ int r=0,c=getchar(); while(!isdigit(c))c=getchar(); while(isdigit(c)) r=r*10+c-‘0‘,c=getchar(); return r; } int n,m,k,f; /****NTR****/ #define ls ch[x][0] #define rs ch[x][1] int ch[N][2],fa[N],tot; int rev[N],mn[N],w[N],a[N]; void pd(int x){ if(rev[x]){ rev[ls]^=1,rev[rs]^=1; rev[x]=0;swap(ls,rs); } } void pp(int x){ mn[x]=w[x]; if(ls)mn[x]=min(mn[x],mn[ls]); if(rs)mn[x]=min(mn[x],mn[rs]); } int isroot(int x){ return x!=ch[fa[x]][0]&&x!=ch[fa[x]][1]; } int get(int x){ return x==ch[fa[x]][1]; } void rotate(int x){ int y=fa[x],z=fa[y],px=get(x),py=get(y); int t=ch[x][px^1]; if(!isroot(y))ch[z][py]=x;fa[x]=z; ch[x][px^1]=y,fa[y]=x; ch[y][px]=t,fa[t]=y; pp(y);pp(x); } int s[N],top; void splay(int x){ top=0;s[++top]=x; for(int i=x;!isroot(i);i=fa[i])s[++top]=fa[i]; for(int i=top;i;i--)pd(s[i]); while(!isroot(x)){ int y=fa[x]; if(!isroot(y)) rotate(get(x)==get(y)?y:x); rotate(x); } } void access(int x){ for(int las=0;x;las=x,x=fa[x]){ splay(x);rs=las;pp(x); } } void makeroot(int x){ access(x);splay(x);rev[x]^=1; } void link(int x,int y){ makeroot(x);fa[x]=y; } void cut(int x,int y){ makeroot(x);access(y);splay(y); fa[x]=ch[y][0]=0; } int find(int x){ access(x),splay(x); while(ls)x=ls; return x; } int getmin(int x,int y){ makeroot(x);access(y);splay(y); return mn[y]; } int p[N],q[N]; void init(){ n=read(),m=read(),k=read(),f=read(); for(int i=1;i<=n;i++)w[i]=mn[i]=1e9; for(int i=1;i<=m;i++)p[i]=read(),q[i]=read(); for(int i=1;i<=m;i++){ w[i+n]=mn[i+n]=i; int u=p[i],v=q[i]; if(u==v){ a[i]=-1; continue; } if(find(u)==find(v)){ int w=getmin(u,v); cut(p[w],w+n); cut(w+n,q[w]); a[i]=w; } link(u,i+n);link(i+n,v); } } #undef ls #undef rs /****hjt***/ int rt[N],ls[N*40],rs[N*40],sum[N*40],cnt; void ins(int &x,int o,int l,int r,int w){ x=++cnt; ls[x]=ls[o],rs[x]=rs[o];sum[x]=sum[o]+1; if(l==r)return; int mid=l+r>>1; if(w<=mid)ins(ls[x],ls[o],l,mid,w); else ins(rs[x],rs[o],mid+1,r,w); } int query(int p,int q,int l,int r,int v){ if(r<=v)return sum[q]-sum[p]; if(l>v)return 0; int mid=l+r>>1,ret=query(ls[p],ls[q],l,mid,v); if(v>mid)ret+=query(rs[p],rs[q],mid+1,r,v); return ret; } void solve(){ for(int i=1;i<=m;i++) ins(rt[i],rt[i-1],0,m,~a[i]?a[i]:m); int ans=0; while(k--){ int l=ans^read(),r=ans^read(); ans=query(rt[l-1],rt[r],0,m,l-1); ans=n-ans; printf("%d\n",ans);if(!f)ans=0; } } /****main****/ int main(){ init(); solve(); }