【bzoj3514】【Codechef MARCH14 GERALD07加强版】【lct+主席树】

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。

题解:我们依次加入每条边。

           对于边i.

           如果i加入之后不会形成环就直接加进去。

           否则切开这个环上最早加入的边j,并记录p[i]=j表示i加入切开了j.

           这个数组显然可以用lct处理。

           对于每次询问,考虑l-r中每条边的贡献。

           可以发现如果p[i]<l则这条边的贡献是-1.

           因为这条边加入势必会将两个联通块接起来。

           所以我们只要统计出来l-r中p[i]<l的数量。然后用n减去这个数即可。

           这个可以用主席树来处理。

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#define N 400010
using namespace std;
int sz,c[N][2],st[N],fa[N],rev[N],v[N],ls[N*20],rs[N*20],root[N],sum[N*20];
int p[N],mn[N],q,kind,n,m,lastans,x,y;
struct use{int st,en;}e[N<<1];
bool isroot(int x){return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;}
void updata(int x){
  int l=c[x][0],r=c[x][1];mn[x]=x;
  if (v[mn[l]]<v[mn[x]]) mn[x]=mn[l];
  if (v[mn[r]]<v[mn[x]]) mn[x]=mn[r];
}
void pushdown(int x){
  int l=c[x][0],r=c[x][1];
  if (rev[x]){
    rev[l]^=1;rev[r]^=1;rev[x]^=1;
    swap(c[x][0],c[x][1]);
  }
}
void rotata(int x){
  int y=fa[x],z=fa[y],l,r;
  if (c[y][0]==x) l=0;else l=1;r=l^1;
  if (!isroot(y)){if (c[z][0]==y)c[z][0]=x;else c[z][1]=x;}
  fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
  c[y][l]=c[x][r];c[x][r]=y;
  updata(y);updata(x);
}
void splay(int x){
 int top(0);st[++top]=x;
 for (int i=x;!isroot(i);i=fa[i]) st[++top]=fa[i];
 for (int i=top;i;i--) pushdown(st[i]);
 while (!isroot(x)){
   int y=fa[x],z=fa[y];
   if (!isroot(y)){
    if (c[y][0]==x^c[z][0]==y) rotata(x);
    else rotata(y);
   } 
   rotata(x);
 }
}
void access(int x){int t(0);while (x){splay(x);c[x][1]=t;t=x;updata(x);x=fa[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);c[y][0]=fa[x]=0;}
int que(int x,int y){makeroot(y);access(x);splay(x);return mn[x];}
int find(int x){
  access(x);splay(x);
  int y=x;
  while (c[y][0]) y=c[y][0];
  return y;
}
void add(int l,int r,int last,int &y,int x){
 y=++sz;sum[y]=sum[last]+1;
 if (l==r) return;
 ls[y]=ls[last];rs[y]=rs[last];
 int mid=(l+r)>>1;
 if (x<=mid) add(l,mid,ls[last],ls[y],x);
 else add(mid+1,r,rs[last],rs[y],x);
}
int query(int l,int r,int x,int y,int val){
 if(r==val)return sum[y]-sum[x];
 int mid=(l+r)>>1;
 if(val<=mid)return query(l,mid,ls[x],ls[y],val);
 else return sum[ls[y]]-sum[ls[x]]+query(mid+1,r,rs[x],rs[y],val);
}
void pre(){
 int num=n;
 for (int i=1;i<=m;i++){
   int x=e[i].st,y=e[i].en;
   if (x==y) {p[i]=i;continue;} 
   if (find(x)==find(y)){
     int u=que(x,y),t=v[u];
     p[i]=t;cut(e[t].st,u);cut(e[t].en,u);
   }
   v[++num]=i;mn[num]=num;
   link(x,num);link(y,num);
 }
 for (int i=1;i<=m;i++)
   add(0,m,root[i-1],root[i],p[i]);
}
void solve(int x,int y){
  if (kind==1) x^=lastans,y^=lastans;
  lastans=n-query(0,m,root[x-1],root[y],x-1);
  printf("%d\n",lastans);
}
int main(){
   memset(v,127/3,sizeof(v));
   scanf("%d%d%d%d",&n,&m,&q,&kind);
   for (int i=1;i<=n+m;i++) mn[i]=i;
   for (int i=1;i<=m;i++)
    scanf("%d%d",&e[i].st,&e[i].en);
   pre();  
   for (int i=1;i<=q;i++){
     scanf("%d%d",&x,&y);
     solve(x,y);  
   }
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值