BZOJ3514:Codechef MARCH14 GERALD07加强版

3514: Codechef MARCH14 GERALD07加强版

Time Limit: 60 Sec  Memory Limit: 256 MB
Submit: 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

Sample Output

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

 

  

 

posted @ 2017-07-31 21:35  QYP_2002  阅读(178)  评论(0编辑  收藏  举报