题意:
N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数。
分析:
据说有dalao会离线做这题?
看到L和R就能想到主席树?dalao们太强了……
如果我们给出n个点,m条边,求整张图的联通块个数,那么可以维护一个并查集,假如合并了p次,那么最终结果就是n-p。
但是,如果求保留[L,R]这些边图中的联通块个数怎么办?因为边的序号是有序的,所以我们肯定是要把边一次连入图中的。
设我们新要加入的某条边两端点是x和y,可能我们加入这第i条边时,x和y已经连通,那么我们可以将x到y链上最早加入的一条边删除,假设删除的那条边序号为j,那么我们令p[i]=j,当然,如果x和y原本不连通的话,p[i]=0。
为什么要求这个呢。我们发现,对于存在于L到R区间内的边i,所有的i不大于L-1的p[i],对答案的贡献都是-1(包括p[i]==0的情况)。为什么呢? 我们可以看到,如果在这个区间内p[i]
这个过程听起来并不复杂,但是,怎样维护,怎样在合适的时间复杂度内求出解成了我们面临的问题。
加边删边操作用LCT显然可以解决。但是求L到R内p[i]不超过L-1的边的数量怎么办,我们可以用主席树对吧,毕竟这相当于建n棵彼此相似的线段树,只要用到前缀和的思想即可(就像区间k大那样就好)。
代码:
![](http://img.e-com-net.com/image/info8/b8d97b5613f94ed2ba791cad57d0b2ed.gif)
![](http://img.e-com-net.com/image/info8/2f88dd3f1cd145f59c0e47b51acdbd4b.gif)
1 #include2 #define lc(x) t[x][0] 3 #define rc(x) t[x][1] 4 using namespace std; 5 const int N=400005; 6 struct node{int x,y;}e[N*2]; 7 struct LCT{ 8 int t[N][2],s[N],rev[N],fa[N],v[N],mn[N],tp; 9 void init(){memset(v,0x3f,sizeof(v));} 10 void pushup(int x){ 11 int l=lc(x),r=rc(x);mn[x]=x; 12 if(v[mn[l]] mn[l]; 13 if(v[mn[r]] mn[r]; 14 } bool pdrt(int x){ 15 return rc(fa[x])!=x&&lc(fa[x])!=x; 16 } void revers(int x){ 17 rev[x]^=1;swap(lc(x),rc(x)); 18 } void pushdown(int x){ 19 if(rev[x]){rev[x]=0; 20 if(lc(x)) revers(lc(x)); 21 if(rc(x)) revers(rc(x)); 22 } return ; 23 } void rotate(int x){ 24 int y=fa[x];int z=fa[y]; 25 int dy=(rc(y)==x),dz=(rc(z)==y); 26 if(!pdrt(y)) t[z][dz]=x; 27 t[y][dy]=t[x][dy^1];fa[t[y][dy]]=y; 28 t[x][dy^1]=y;fa[y]=x;fa[x]=z; 29 pushup(x);pushup(y); 30 } void splay(int x){ 31 s[++tp]=x; 32 for(int i=x;!pdrt(i);i=fa[i]) 33 s[++tp]=fa[i]; 34 while(tp) pushdown(s[tp--]); 35 while(!pdrt(x)){ 36 int y=fa[x];int z=fa[y]; 37 if(!pdrt(y)) 38 if(rc(y)==x^rc(z)==y) rotate(x); 39 else rotate(y);rotate(x); 40 } pushup(x);return ; 41 } void access(int x){ 42 for(int i=0;x;x=fa[x]) 43 splay(x),rc(x)=i,i=x; 44 } void mkrt(int x){ 45 access(x);splay(x);revers(x); 46 } int fdrt(int x){ 47 access(x);splay(x); 48 while(lc(x)) pushdown(x),x=lc(x); 49 return x; 50 } void split(int x,int y){ 51 mkrt(x);access(y);splay(y); 52 } void link(int x,int y){ 53 mkrt(x);if(fdrt(y)!=x) fa[x]=y; 54 } void cut(int x,int y){ 55 mkrt(x); 56 if(fdrt(y)==x&&fa[x]==y&&!rc(x)) 57 fa[x]=lc(y)=0;pushup(y); 58 } int ask(int x,int y){ 59 split(x,y);return mn[y]; 60 } 61 } lct;int rt[N],cnt=0,n,m,tp,q,ans=0; 62 struct ch{int l,r,sm;}t[N*20];int p[N]; 63 int update(int cur,int fa,int l,int r,int p){ 64 cur=++cnt;t[cur]=t[fa]; 65 t[cur].sm++;if(l==r) return cur; 66 int mid=l+r>>1; 67 if(p<=mid) t[cur].l= 68 update(t[cur].l,t[fa].l,l,mid,p); 69 else t[cur].r= 70 update(t[cur].r,t[fa].r,mid+1,r,p); 71 return cur; 72 } int query(int x,int y,int l,int r,int v){ 73 if(r==v) return t[y].sm-t[x].sm; 74 int mid=l+r>>1; 75 if(v<=mid) 76 return query(t[x].l,t[y].l,l,mid,v); 77 else return t[t[y].l].sm-t[t[x].l].sm+ 78 query(t[x].r,t[y].r,mid+1,r,v); 79 } void pre(){ 80 int num=n; 81 for(int i=1;i<=m;i++){ 82 int x=e[i].x,y=e[i].y; 83 if(x==y){p[i]=i;continue;} 84 if(lct.fdrt(x)==lct.fdrt(y)){ 85 int u=lct.ask(x,y); 86 int v=lct.v[u];p[i]=v; 87 lct.cut(e[v].x,u); 88 lct.cut(e[v].y,u); 89 } lct.v[++num]=i;lct.mn[num]=num; 90 lct.link(x,num);lct.link(y,num); 91 } for(int i=1;i<=m;i++) 92 rt[i]=update(rt[i],rt[i-1],0,m,p[i]); 93 } void solve(int x,int y){ 94 if(tp==1) x^=ans,y^=ans; 95 ans=n-query(rt[x-1],rt[y],0,m,x-1); 96 printf("%d\n",ans); 97 } int main(){ 98 lct.init(); 99 scanf("%d%d%d%d",&n,&m,&q,&tp); 100 for(int i=1;i<=n+m;i++) lct.mn[i]=i; 101 for(int i=1;i<=m;i++) 102 scanf("%d%d",&e[i].x,&e[i].y);pre(); 103 for(int i=1,x,y;i<=q;i++) 104 scanf("%d%d",&x,&y),solve(x,y);return 0; 105 }