• Codeforces 482E ELCA (LCT)


    题目链接

    http://codeforces.com/contest/482/problem/E

    题解

    T2智商题T3大LCT题,我一个也不会= =
    CF的标算好像是分块?反正现在LCT都普及了就用LCT好了。

    首先算期望推个式子,易得答案为(sum_u a[u](sz[u]^2-sum_{vin son[u]} sz[v]^2)) ((sz)为子树大小),令求和的那个东西等于(f[u])
    并且如果往一个(u)里新添一个儿子(v),增添后的子树大小是(sz[v]), 那么新增的答案是(2a[u]sz[v](sz[u]-sz[v]))
    然后我们要支持换父亲,动态维护这个东西

    后面的就是莽上一个LCT。
    这个只能详见代码,解释一下代码里变量的含义
    对于一个Splay结构体(u):
    (ans): 这个点Splay的子树中所有点的(f)之和。Splay的根的(ans)就是要求的答案。
    (sz01): 这个点所有虚子树的大小之和加上本身的(1)。((sz)是原树中子树大小)
    (sz02): 这个点本身所有虚子树的大小平方和,不包括本身。
    (ans0): 这个点本身所有虚儿子的(ans)之和。
    (sz11): 这个点所有实儿子和虚儿子的大小之和。
    (sum): 这个点及其所有实儿子(v)(sz01[v] imes a[v])之和。
    这些变量看起来有些匪夷所思,那么我们看下怎么维护。
    首先,所有对虚儿子求和的变量(也就是名字里带0的三个)都在access时处理,pushup时无需处理。
    现在考虑pushup那个函数,前两行处理的是(sz11)(sum), 这个根据定义求就行,也没有什么好说的。
    后四行是重点——(ans)的更新。

    我们把(ans[u])分了四部分统计。(选两个点(x)(y)(a[lca(x,y)])之和)
    第一部分: 两个点都选在(u)的祖先(splay左子树内),或都选在splay右子树内,或都选在(u)的同一棵虚子树内。

    spl[u].ans = spl[ls].ans+spl[u].ans0+spl[rs].ans;
    

    第二部分: 两个点选在(u)的两棵不同虚子树内。

    spl[u].ans += (spl[u].sz01*spl[u].sz01-spl[u].sz02)*a[u];
    

    第三部分: 两个点之一选在(u)的虚子树内,另一个点选在(u)的splay右子树内。这样LCA一定是(u).

    spl[u].ans += 2ll*spl[u].sz01*spl[rs].sz11*a[u];
    

    第四部分: 两个点之一选在(u)的祖先(splay左子树)或其虚子树内,另一个选在(u)的整个splay子树及子树内所有点的虚子树去掉(u)上方(splay左子树内及其虚子树)的部分,也就是(u)及其下方(splay右子树内)所有点及其虚子树内。

    spl[u].ans += 2ll*spl[ls].sum*(spl[u].sz11-spl[ls].sz11);
    

    于是就做完了。(第四部分确实有点复杂)

    最后膜拜一发考场切此题的新初三巨佬
    zjr nb!

    代码

    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #define llong long long
    using namespace std;
     
    void read(int &x)
    {
        int f=1;x=0;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
        x*=f;
    }
     
    const int N = 1e5;
    struct SplayNode
    {
        int son[2],fa; llong sz01,sz11,sz02,sum,ans0,ans;
    } spl[N+3];
    int fa[N+3];
    llong a[N+3];
    int n,q;
     
    bool isroot(int u) {return spl[spl[u].fa].son[0]!=u && spl[spl[u].fa].son[1]!=u;}
     
    void pushup(int u)
    {
        int ls = spl[u].son[0],rs = spl[u].son[1];
        spl[u].sz11 = spl[ls].sz11+spl[u].sz01+spl[rs].sz11;
        spl[u].sum = spl[ls].sum+spl[rs].sum+spl[u].sz01*a[u];
        spl[u].ans = spl[ls].ans+spl[u].ans0+spl[rs].ans;
        spl[u].ans += (spl[u].sz01*spl[u].sz01-spl[u].sz02)*a[u];
        spl[u].ans += 2ll*spl[u].sz01*spl[rs].sz11*a[u];
        spl[u].ans += 2ll*spl[ls].sum*(spl[u].sz11-spl[ls].sz11);
    }
     
    void rotate(int u)
    {
        int x = spl[u].fa,y = spl[x].fa;
        spl[u].fa = y;
        if(!isroot(x)) {spl[y].son[x==spl[y].son[1]] = u;}
        int dir = u==spl[x].son[0];
        spl[x].son[dir^1] = spl[u].son[dir];
        if(spl[u].son[dir]) {spl[spl[u].son[dir]].fa = x;}
        spl[u].son[dir] = x; spl[x].fa = u;
        pushup(x); pushup(u);
    }
     
    void splaynode(int u)
    {
        while(!isroot(u))
        {
            int x = spl[u].fa,y = spl[x].fa;
            if(!isroot(x)) {x==spl[y].son[1] ^ u==spl[x].son[1] ? rotate(u) : rotate(x);}
            rotate(u);
        }
        pushup(u);
    }
     
    void access(int u)
    {
        for(int i=0; u; i=u,u=spl[u].fa)
        {
            splaynode(u);
            int ls = spl[u].son[0],rs = spl[u].son[1];
            spl[u].sz01 += spl[rs].sz11;
            spl[u].sz02 += spl[rs].sz11*spl[rs].sz11;
            spl[u].ans0 += spl[rs].ans; //not ans0
            rs = spl[u].son[1] = i;
            spl[u].sz01 -= spl[rs].sz11;
            spl[u].sz02 -= spl[rs].sz11*spl[rs].sz11;
            spl[u].ans0 -= spl[rs].ans;
            pushup(u);
        }
    }
     
    void link(int u,int v)
    {
        access(v); splaynode(v);
        access(u); splaynode(u); //in order to pushup
        spl[u].sz01 += spl[v].sz11;
        spl[u].sz02 += spl[v].sz11*spl[v].sz11;
        spl[u].ans0 += spl[v].ans;
        spl[v].fa = u;
        pushup(u);
    }
     
    void cut(int u,int v)
    {
        access(u); splaynode(u);
        splaynode(v); //v is in the virtual subtree of u
        spl[u].sz01 -= spl[v].sz11;
        spl[u].sz02 -= spl[v].sz11*spl[v].sz11;
        spl[u].ans0 -= spl[v].ans;
        spl[v].fa = 0;
        pushup(u);
    }
     
    bool isanc(int u,int v) //if u is ancestor of v
    {
        access(v); splaynode(v);
        splaynode(u);
        if(!isroot(v)) return true;
        return false;
    }
     
    void printans(llong x)
    {
        double ans = (double)x/(double)n/(double)n;
        printf("%.12lf
    ",ans);
    }
     
    int main()
    {
        scanf("%d",&n);
        for(int i=2; i<=n; i++) scanf("%d",&fa[i]);
        for(int i=1; i<=n; i++) scanf("%I64d",&a[i]);
        for(int i=1; i<=n; i++) spl[i].sz11 = spl[i].sz01 = 1ll,spl[i].ans = spl[i].sum = a[i];
        for(int i=2; i<=n; i++)
        {
            link(fa[i],i);
        }
        access(1); splaynode(1);
        printans(spl[1].ans);
        scanf("%d",&q);
        for(int i=1; i<=q; i++)
        {
            char opt[5]; scanf("%s",opt+1);
            if(opt[1]=='P')
            {
                int x,y; scanf("%d%d",&x,&y);
                if(isanc(x,y)) {swap(x,y);}
                cut(fa[x],x);
                fa[x] = y;
                link(fa[x],x);
                access(1); splaynode(1);
                printans(spl[1].ans);
            }
            else if(opt[1]=='V')
            {
                int x; llong y; scanf("%d%I64d",&x,&y);
                access(x); splaynode(x);
                a[x] = y;
                pushup(x);
                printans(spl[x].ans);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    5年多了
    处女作
    2.2 Multinomial variables多项变量的分布_PRML模式识别与机器学习读书笔记
    2.1 Binary variables 二元变量分布_PRML模式识别与机器学习读书笔记
    1.1 Example:Polynomial Curve Fitting 多项式曲线拟合_PRML模式识别与机器学习读书笔记
    1 Introduction_PRML模式识别与机器学习读书笔记
    操作系统概述
    TFS2015创建项目
    PowerDesigner
    python实用函数
  • 原文地址:https://www.cnblogs.com/suncongbo/p/11330695.html
Copyright © 2020-2023  润新知