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;
}