BZOJ4530: [Bjoi2014]大融合(LCT维护子树,线段树合并)

来源:互联网 发布:js点击图片全屏放大 编辑:程序博客网 时间:2024/05/16 03:12

传送门

题意:
给一个动态树,维护sze集合大小。

题解:
动态树。

LCT维护子树和的方法(不支持子树修改,其实实现起来也挺简单的):

对于维护这一类满足加减的信息,可以考虑在LCT的每个点分别维护出所有虚边和实边连向他和。
如果得到了维护,那么可以轻松在O(logn)时间内完成询问。
考虑怎么维护:
首先Splay不会改变虚边的性质,那么Splay中直接动态调整实边的和就好了。
对于Access操作,显然断开右儿子会导致虚实边切换,可以在维护的东西上简单加减就好了。不过需要注意的是,加减最好在当前点是其所在Splay的根节点的时候才能加减,否则该点的和改变后其父节点的和也会相应改变(只有实父亲),还要向上 update(虚父亲不用更新是因为Access操作会一路向上走,总会更新到虚父亲的)。
对于Link操作,直接连边也会导致一样的错误,那么先将两边的点都置为根节点就好了。
对于每组查询操作,先将查询点Access,此时其虚子树和包含了其所有子树,可以加上自己的信息而得到答案。

关于维护最大最小值也可以用类似的方法。不过需要注意的是不能简单的加减,容易想到增加一个logn的时间复杂度,对每个点维护multiset来完成删除操作。

  • Code
#include<bits/stdc++.h>using namespace std;inline int rd(){    char ch=getchar();int i=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}    while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}    return i*f;}inline void W(long long x){    static int buf[50];    if(!x){putchar('0');return;}    if(x<0){putchar('-');x=-x;}    while(x){buf[++buf[0]]=x%10,x/=10;}    while(buf[0]){putchar(buf[buf[0]--]+'0');}}const int Maxn=1e5+50;int n,m;struct node{    node *lc,*rc,*fa;    int sze_v,sze_t,revtag;    node():lc(NULL),rc(NULL),fa(NULL),sze_t(1){}    inline void rev();inline void upt();inline void pd();}Pool[Maxn],*pool=Pool;inline void node::upt(){sze_t=(lc?lc->sze_t+lc->sze_v:0)+(rc?rc->sze_t+rc->sze_v:0)+1;}inline void node::rev(){revtag^=1;swap(lc,rc);}inline bool isroot(node *x){return !x->fa||(x->fa->lc!=x&&x->fa->rc!=x);}inline bool which(node *x){return x->fa->lc==x;}inline void node::pd(){    if(!isroot(this))fa->pd();    if(revtag){        if(lc)lc->rev();if(rc)rc->rev();        revtag=0;    }}inline void rotate(node *x){    node *y=x->fa,*z=y->fa;    if(!isroot(y))((z->lc==y)?z->lc:z->rc)=x;    x->fa=z;y->fa=x;    if(y->lc==x){        node *b=x->rc;        x->rc=y;y->lc=b;        if(b)b->fa=y;    }else{        node *b=x->lc;        x->lc=y;y->rc=b;        if(b)b->fa=y;    }    y->upt();x->upt();}inline void splay(node *x){    x->pd();    while(!isroot(x)){        node *y=x->fa;        if(!isroot(y)){            (which(y)^which(x))?(rotate(x)):(rotate(y));        }rotate(x);    }}inline void access(node *x){    for(node *t=NULL;x;t=x,x=x->fa){        splay(x);int rc_sze=(x->rc)?(x->rc->sze_t+x->rc->sze_v):0;        x->sze_t-=rc_sze;x->sze_v+=rc_sze;        x->rc=t;rc_sze=(x->rc)?(x->rc->sze_t+x->rc->sze_v):0;        x->sze_t+=rc_sze;x->sze_v-=rc_sze;    }}inline void makeroot(node *x){    access(x);splay(x);x->rev();}inline void link(node *x,node *y){    makeroot(x);makeroot(y);x->fa=y;    y->sze_v+=(x->sze_v+x->sze_t);}inline void query(node *x,node *y){    makeroot(x);int sze1=x->sze_t+x->sze_v;    access(y);int sze2=y->sze_v+1;    W(1ll*(sze1-sze2)*sze2);putchar('\n');}int main(){    n=rd(),m=rd();    for(int i=1;i<=m;i++){        static char ch[2];        scanf("%s",ch+1);        (ch[1]=='A')?(link(pool+rd(),pool+rd())):(query(pool+rd(),pool+rd()));    }}

线段树合并一样可以处理,考虑先把原树的关系处理出来,对于当前联通快,按照dfs序查询两颗子树就好了。

#include<bits/stdc++.h>using namespace std;struct IO{    streambuf *ib,*ob;    int buf[50];    inline void init()    {        ios::sync_with_stdio(false);        cin.tie(NULL);cout.tie(NULL);        ib=cin.rdbuf();ob=cout.rdbuf();    }    inline int read()    {        char ch=ib->sbumpc();int i=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=ib->sbumpc();}        while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=ib->sbumpc();}        return i*f;    }    inline void W(long long x)    {        if(!x){ob->sputc('0');ob->sputc('\n');return;}        if(x<0){ob->sputc('-');x=-x;}        while(x){buf[++buf[0]]=x%10;x/=10;}        while(buf[0]){ob->sputc(buf[buf[0]--]+'0');}        ob->sputc('\n');    }}io;const int Maxn=1e5+50;int n,m,ecnt,rt[Maxn],dfn[Maxn],dep[Maxn],anc[Maxn],ind,out[Maxn],tot;int sze[Maxn*20],lc[Maxn*20],rc[Maxn*20];struct Q{    int k,x,y;}q[Maxn];vector<int>edge[Maxn];inline void dfs(int now,int f){    dfn[now]=++ind;dep[now]=dep[f]+1;    for(int e=edge[now].size()-1;e>=0;e--)    {        int v=edge[now][e];        if(v==f)continue;        dfs(v,now);    }    out[now]=ind;}inline void update(int now){    sze[now]=sze[lc[now]]+sze[rc[now]];}inline int getf(int now){    return (anc[now]==now)?(now):(anc[now]=getf(anc[now]));}inline void insert(int now,int l,int r,int pos){    if(l==r)    {        sze[now]=1;        return;    }    int mid=(l+r)>>1;    if(pos<=mid)insert(lc[now]=++tot,l,mid,pos);    else insert(rc[now]=++tot,mid+1,r,pos);    update(now);}inline int merge(int x,int y){    if(!y)return x;    if(!x)return y;    sze[x]+=sze[y];    lc[x]=merge(lc[x],lc[y]);    rc[x]=merge(rc[x],rc[y]);    return x;}inline int query(int now,int l,int r,int L,int R){    if(L<=l&&r<=R)return sze[now];    int mid=(l+r)>>1;    int res=0;    if(L<=mid&&lc[now])res+=query(lc[now],l,mid,L,R);    if(R>mid&&rc[now])res+=query(rc[now],mid+1,r,L,R);    return res;}int main(){    io.init();n=io.read(),m=io.read();    for(int i=1;i<=n;i++)anc[i]=i;    for(int i=1;i<=m;i++)    {        static char ch[3];        cin>>(ch+1);        q[i].k=((ch[1]=='A')?1:2),q[i].x=io.read(),q[i].y=io.read();        if(q[i].k==1)        {            edge[q[i].x].push_back(q[i].y);            edge[q[i].y].push_back(q[i].x);        }    }    for(int i=1;i<=n;i++)if(!dfn[i])dfs(i,0);    for(int i=1;i<=n;i++)    {        rt[i]=++tot;        insert(rt[i],1,n,dfn[i]);    }    for(int i=1;i<=m;i++)    {        if(dep[q[i].x]>dep[q[i].y])swap(q[i].x,q[i].y);        if(q[i].k==1)        {            rt[getf(q[i].x)]=merge(rt[getf(q[i].x)],rt[getf(q[i].y)]);            anc[getf(q[i].y)]=anc[q[i].x];        }        else        {            int k=getf(q[i].y);            int v=query(rt[k],1,n,dfn[q[i].y],out[q[i].y]);            io.W(1ll*(sze[rt[k]]-v)*v);        }    }}