BZOJ 3514 Codechef MARCH14 GERALD07 加强版 LCT+主席树

题意:

  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]<L,则相当于这条边的假如使两个原本不连通的点连通了起来,或是之前连通但是维系两个点连通的边不在这个区间内(等同于加边时被替掉了),即对答案是有贡献的,但如果L<=p[i]<=R,则相当于这个区间内的边自己人和自己人形成了环,那么对答案不会造成贡献,所以我们答案是正确的,有几个满足要求的p[i],就用n减去几即可。

  这个过程听起来并不复杂,但是,怎样维护,怎样在合适的时间复杂度内求出解成了我们面临的问题。

  加边删边操作用LCT显然可以解决。但是求L到R内p[i]不超过L-1的边的数量怎么办,我们可以用主席树对吧,毕竟这相当于建n棵彼此相似的线段树,只要用到前缀和的思想即可(就像区间k大那样就好)。

代码:

  1 #include<bits/stdc++.h>
  2 #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]]<v[mn[x]]) mn[x]=mn[l];
 13         if(v[mn[r]]<v[mn[x]]) mn[x]=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 }
LCT+主席树

 

转载于:https://www.cnblogs.com/Alan-Luo/articles/10158613.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值