SPOJQTREE6(Link-Cut-Tree)

Posted 小蒟蒻yyb的博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SPOJQTREE6(Link-Cut-Tree)相关的知识,希望对你有一定的参考价值。

【SPOJ】QTREE6(Link-Cut-Tree)

题面

Vjudge

题解

很神奇的一道题目
我们发现点有黑白两种,又是动态加边/删边
不难想到\(LCT\)

最爆力的做法,显然是每次修改单点颜色的时候
暴力修改当前点和它的父亲以及儿子之间的连边状态

但是这样显然是假的(菊花树了解一下)

怎么优化呢?
对于每次操作,我们考虑如何只修改一次。
对于树上的一个结点,如果只修改一次,显然是修改和其父亲的状态。
那么,我们在考虑\(LCT\)的连边操作的时候,
如果当前点变色,那么就只修改和它父亲的连边。
这样怎么算答案呢?
如果我们确定树是一棵有根树
那么,我们只需要找到当前点深度最浅的父亲
这个父亲在当前颜色的树上的儿子个数显然就是答案

所以,我们只需要每次只修改当前点和其父亲的关系就行了。
但是要注意一个问题,因为强制是有根树了。
所以打死都不能有\(makeroot\)操作
所以\(link,cut\)之类的都要魔改一发了。。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 111111
#define ls (t[x].ch[0])
#define rs (t[x].ch[1])
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
struct Line{int v,next;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
struct Link_Cut_Tree
{
    struct Node
    {
        int ch[2],ff;
        int size,sum;
        int rev;
    }t[MAX];
    bool isroot(int x){return t[t[x].ff].ch[0]!=x&&t[t[x].ff].ch[1]!=x;}
    void pushup(int x){t[x].sum=t[ls].sum+t[rs].sum+t[x].size+1;}
    void rotate(int x)
    {
        int y=t[x].ff,z=t[y].ff;
        int k=t[y].ch[1]==x;
        if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;t[x].ff=z;
        t[y].ch[k]=t[x].ch[k^1];t[t[x].ch[k^1]].ff=y;
        t[x].ch[k^1]=y;t[y].ff=x;
        pushup(y);pushup(x);
    }
    void Splay(int x)
    {
        while(!isroot(x))
        {
            int y=t[x].ff,z=t[y].ff;
            if(!isroot(y))
                (t[y].ch[0]==x)^(t[z].ch[0]==y)?rotate(x):rotate(y);
            rotate(x);
        }
        pushup(x);
    }
    void access(int x)
    {
        for(int y=0;x;y=x,x=t[x].ff)
        {
            Splay(x);t[x].size+=t[rs].sum-t[y].sum;
            rs=y;pushup(x);
        }
    }
    void link(int x,int y){if(!y)return;access(y);Splay(x);Splay(y);t[x].ff=y;t[y].size+=t[x].sum;pushup(y);}
    void cut(int x,int y){if(!y)return;access(x);Splay(x);ls=t[ls].ff=0;pushup(x);}
    int findroot(int x){access(x);Splay(x);while(ls)x=ls;Splay(x);return x;}
}LCT[2];
int n,m,fa[MAX],c[MAX];
void dfs(int u,int ff)
{
    for(int i=h[u];i;i=e[i].next)
    {
        int v=e[i].v;if(v==ff)continue;
        LCT[1].link(v,u);fa[v]=u;
        dfs(v,u);
    }
}
int main()
{
    n=read();
    for(int i=1;i<=n;++i)c[i]=1;
    for(int i=1,u,v;i<n;++i)u=read(),v=read(),Add(u,v),Add(v,u);
    dfs(1,0);
    m=read();
    while(m--)
    {
        int opt=read(),x=read();
        if(opt)LCT[c[x]].cut(x,fa[x]),c[x]^=1,LCT[c[x]].link(x,fa[x]);
        else
        {
            LCT[c[x]].access(x);
            int ff=LCT[c[x]].findroot(x);
            if(c[ff]==c[x])printf("%d\n",LCT[c[x]].t[ff].sum);
            else printf("%d\n",LCT[c[x]].t[LCT[c[x]].t[ff].ch[1]].sum);
        }
    }
    return 0;       
}

以上是关于SPOJQTREE6(Link-Cut-Tree)的主要内容,如果未能解决你的问题,请参考以下文章

Link-Cut-Tree 动态树算法

Link-cut-Tree

Link-Cut-Tree 题目总结

对于动态树 (Link-Cut-Tree, LCT) 的理解与总结

bzoj2049[Sdoi2008]Cave 洞穴勘测 link-cut-tree

bzoj1036: [ZJOI2008]树的统计Count link-cut-tree版