Advertisement

ACM-ICPC 2018 南京赛区网络预赛 An Easy Problem On The Trees [LCT]

阅读量:

题意

给你n个节点的一颗树,m个操作,操作有三种:
1. 连接a与b。
2. 以x为根节点,将b与其父节点连接的边删除。
3. 询问从x开始最后走回x的期望步数(每条边等概率移动)。

题解

对于询问3可以推出一个结论,期望的步数是2*(size(x)-1)/du(x),通过这个结论我们只需要用LCT维护子树大小即可。

AC代码

复制代码
    #include<stdio.h>
    #include<algorithm>
    #define N 100005
    #define MOD 998244353
    using namespace std;
    typedef long long ll;
    //struct stp{ll x,y,a,b;}a[N];
    //bool cmp(stp a,stp b){return a.a<b.a;}
    ll rev[N],ch[N][2],V[N],pre[N],fa[N],st[N],t,sizesum[N],n,sizexu[N],du[N];
    //splay中数组:rev反转标记 ch树的儿子节点
    //V节点value pre节点的父节点 st存节点的栈
    //其他数组:fa
    ll find(ll x){return fa[x]==x?x:fa[x]=find(fa[x]);}//并查集
    bool is_root(ll x){return ch[pre[x]][0]!=x&&ch[pre[x]][1]!=x;}
    //判断x是否为其中一颗splay的根节点
    void reverse(ll x)
    {
    rev[x]^=1;swap(ch[x][0],ch[x][1]);
    rev[ch[x][0]]^=1;rev[ch[x][1]]^=1;
    }
    void update(ll x)
    {
    sizesum[x]=sizexu[x]+sizesum[ch[x][0]]+sizesum[ch[x][1]]+1;
    }
    void rotate(ll x)
    {
    ll y=pre[x],z=pre[y];
    bool f=ch[y][1]==x;
    if(!is_root(y))ch[z][ch[z][1]==y]=x;
    ch[y][f]=ch[x][!f];ch[x][!f]=y;
    pre[x]=z;pre[y]=x;pre[ch[y][f]]=y;
    update(y);update(x);
    }
    void splay(ll x)
    {
    st[t=1]=x;
    for(ll y=x;!is_root(y);st[++t]=y=pre[y]);
    for(;t;t--)if(rev[st[t]])reverse(st[t]);
    for(;!is_root(x);rotate(x))if(!is_root(pre[x]))
        ch[pre[x]][0]==x^ch[pre[pre[x]]][0]==pre[x]?rotate(x):rotate(pre[x]);
    update(x);
    }
    void access(ll x)
    {
    for(ll t=0;x;ch[x][1]=t,t=x,x=pre[x])
    {
        if(t)update(t);
        splay(x);
        sizexu[x]+=sizesum[ch[x][1]]-sizesum[t];
    }
    }
    void make_root(ll x){access(x);splay(x);rev[x]^=1;}
    void link(ll x,ll y){make_root(x);make_root(y);pre[x]=y;du[x]++,du[y]++;sizexu[y]+=sizesum[x];}//将x的父亲设为y
    void cut(ll x,ll y){make_root(x);access(y);splay(y);pre[x]=ch[y][0]=0;sizesum[y]-=sizesum[x];du[x]--;du[y]--;}
    
    bool is_link(ll x,ll y){
    make_root(x);
    access(y);
    splay(x);
    ll u=y;
    while(pre[u])u=pre[u];
    return u==x;
    }
    ll inv[N];
    int main()
    {
    ll m;
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=n;i++)sizesum[i]=1;
    for(ll i=1;i<n;i++){
        ll u,v;
        scanf("%lld%lld",&u,&v);
        link(u,v);
    }
    inv[1]=1;
    for(ll i=2;i<=n;i++)inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;
    while(m--){
        ll op,x,y;
        scanf("%lld",&op);
        if(op==1){
            scanf("%lld%lld",&x,&y);
            if(!is_link(x,y))
                link(x,y);
            else printf("-1\n");
        }
        else if(op==2){
            scanf("%lld%lld",&x,&y);
            if(is_link(x,y)&&x!=y){
                splay(y);
                if(rev[y])reverse(y);
                ll fa=ch[y][0];
                if(rev[fa])reverse(fa);
                while(ch[fa][1])
                {
                    fa=ch[fa][1];
                    if(rev[fa])reverse(fa);
                }
                cut(y,fa);
            }
            else printf("-1\n");
        }
        else {
            scanf("%lld",&x);
            make_root(x);
            ll ans=inv[du[x]]*(sizesum[x]-1)*2%MOD;
            printf("%lld\n",ans);
        }
    }
    return 0;
    }

全部评论 (0)

还没有任何评论哟~