BZOJ3514:Codechef MARCH14 GERALD07加强版
3514: Codechef MARCH14 GERALD07加强版
Time Limit: 60 Sec Memory Limit: 256 MBSubmit: 1775 Solved: 678
[Submit][Status][Discuss]
Description
N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数。
Input
第一行四个整数N、M、K、type,代表点数、边数、询问数以及询问是否加密。
接下来M行,代表图中的每条边。
接下来K行,每行两个整数L、R代表一组询问。对于type=0的测试点,读入的L和R即为询问的L、R;对于type=1的测试点,每组询问的L、R应为L xor lastans和R xor lastans。
Output
K行每行一个整数代表该组询问的联通块个数。
Sample Input
3 5 4 0
1 3
1 2
2 1
3 2
2 2
2 3
1 5
5 5
1 2
1 3
1 2
2 1
3 2
2 2
2 3
1 5
5 5
1 2
Sample Output
2
1
3
1
1
3
1
HINT
对于100%的数据,1≤N、M、K≤200,000。
思路{
神级思路题.....
首先利用LCT处理出每条边和其他边最早形成的环的编号最小的边.设nxt[x]
那么我们想:对于i∈[l,r]的边,nxt[i]∈[l,r]时,它不会对连通块的数量产生贡献.
对于nxt[i]<l-1的,必定连通了两个原本互不相同的连通块,cnt+1;
那么就是n-cnt,那么要统计cnt,考虑用主席树维护每一条边连通之后的一段区间内nxt[i]的个数.
统计历史版本l-1和当前版本r之间nxt[i]<l的个数.
}
#include<bits/stdc++.h> #define RG register #define il inline #define N 400005 #define rs (ch[x][1]) #define ls (ch[x][0]) using namespace std; int ch[N][2],fa[N],v[N],Min[N],st[N],nxt[N],n,m,k,flag;bool rev[N]; bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;} void up(int x){ Min[x]=x; if(rs)if(v[Min[rs]]<v[Min[x]])Min[x]=Min[rs]; if(ls)if(v[Min[ls]]<v[Min[x]])Min[x]=Min[ls]; } void down(int x){if(rev[x])rev[x]^=1,rev[rs]^=1,rev[ls]^=1,swap(rs,ls);} void Rotate(int x){ int y=fa[x],z=fa[y],l,r; l=ch[fa[x]][1]==x,r=l^1; if(!isroot(y))ch[z][ch[z][1]==y]=x; fa[x]=z,fa[y]=x,ch[y][l]=ch[x][r],fa[ch[x][r]]=y,ch[x][r]=y; up(y),up(x); } void Splay(int x){int top(0),y=x;st[++top]=y; while(!isroot(y))st[++top]=fa[y],y=fa[y]; for(int i=top;i;i--)down(st[i]); while(!isroot(x)){ y=fa[x];int z=fa[y]; if(!isroot(y)){ if(ch[y][0]==x^ch[z][0]==y)Rotate(x); else Rotate(y); }Rotate(x); } } void access(int x){int t=0;while(x)Splay(x),ch[x][1]=t,up(x),t=x,x=fa[x];} int findroot(int x){access(x),Splay(x);while(ch[x][0])x=ch[x][0];return x;} void makeroot(int x){access(x),Splay(x),rev[x]^=1;} void link(int x,int y){makeroot(y),fa[y]=x;} void cut(int x,int y){makeroot(x),access(y),Splay(y),fa[x]=ch[y][0]=0;} int Query(int x,int y){makeroot(x),access(y),Splay(y);return Min[y];} struct ed{int u,v;}e[N];//v存该点中表示边的编号......Min则是当前节点中最小编号的点 int l[N*30],r[N*30],tree[N*30],rt[N*30],lastans,num; #define mid ((L+R)>>1) void Insert(int y,int &x,int L,int R,int val){ x=++num;tree[x]=tree[y]+1;if(L==R)return; l[x]=l[y],r[x]=r[y];if(mid<val)Insert(r[y],r[x],mid+1,R,val); else Insert(l[y],l[x],L,mid,val); } int Query(int y,int x,int L,int R,int val){ if(R<=val)return tree[x]-tree[y]; if(val<=mid)return Query(l[y],l[x],L,mid,val); else return Query(r[y],r[x],mid+1,R,val)+tree[l[x]]-tree[l[y]]; } int main(){ scanf("%d%d%d%d",&n,&m,&k,&flag); for(int i=1;i<=m;++i)scanf("%d%d",&e[i].u,&e[i].v); for(int i=1;i<=n;++i)Min[i]=i,v[i]=666666666;int tot=n; for(int i=1;i<=m;++i){int u=e[i].u,V=e[i].v; if(u==V){nxt[i]=i;continue;} if(findroot(u)==findroot(V)){ int x=Query(u,V),y=v[x]; nxt[i]=y; cut(e[y].u,x),cut(x,e[y].v); }tot++,Min[tot]=tot,v[tot]=i,link(u,tot),link(tot,V); }for(int i=1;i<=m;++i)Insert(rt[i-1],rt[i],0,m,nxt[i]);int ll,rr; for(int i=1;i<=k;++i){ scanf("%d%d",&ll,&rr);if(flag)ll^=lastans,rr^=lastans; lastans=n-Query(rt[ll-1],rt[rr],0,m,ll-1);printf("%d\n",lastans); } return 0; }