• BZOJ4543[POI2014]Hotel加强版——长链剖分+树形DP


    题意参见BZOJ3522

    n<=100000

    数据范围增强了,显然之前的转移方程不行了,那么不妨换一种。

    因为不能枚举根来换根DP,那么我们描述的DP方程每个点要计算三个点都在这个点的子树内的方案数。

    设f[i][j]表示i节点子树中与i距离为j的点的个数.

    g[i][j]表示i节点子树中有g[i][j]对点满足每对点距离他们lca的距离都是d,他们lca距离i节点为d-j

    也就是说现在已经找到两个节点了,需要再在没遍历的i的子树中找到一个距离i为j的点。

    那么很容易得到转移方程:

    ans+=f[x][j-1]*g[to[i]][j]

    ans+=g[x][j+1]*f[to[i]][j]

    g[x][j+1]+=f[to[i]][j]*f[x][j+1]

    g[x][j-1]+=g[to[i]][j]

    f[x][j+1]+=f[to[i]][j]

    发现这个DP是合并深度信息转移的,而且子树信息被合并后就没用了,因此可以用长链剖分优化,每次继承重儿子信息,暴力合并其他子树。

    因为父节点的f和g数组只由重儿子左移一位或右移一位得到,因此O(1)指针优化就好了。

    具体应用到的长链剖分参见长链剖分

    #include<queue>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ll long long
    using namespace std;
    int n;
    int x,y;
    int tot;
    ll s[1000010];
    int head[100010];
    int to[200010];
    int mx[100010];
    int next[200010];
    int son[100010];
    int fa[100010];
    ll ans;
    ll *now=s+1;
    ll *f[100010];
    ll *g[100010];
    void add(int x,int y)
    {
        tot++;
        next[tot]=head[x];
        head[x]=tot;
        to[tot]=y;
    }
    void dfs(int x)
    {
        mx[x]=0;
        for(int i=head[x];i;i=next[i])
        {
            if(to[i]!=fa[x])
            {
                fa[to[i]]=x;
                dfs(to[i]);
                mx[x]=max(mx[to[i]]+1,mx[x]);
                if(mx[to[i]]>mx[son[x]])
                {
                    son[x]=to[i];
                }
            }
        }
    }
    void dfs2(int x)
    {
        if(son[x])
        {
            f[son[x]]=f[x]+1;
            g[son[x]]=g[x]-1;
            dfs2(son[x]);
        }
        f[x][0]=1;
        ans+=g[x][0];
        for(int i=head[x];i;i=next[i])
        {
            if(to[i]!=fa[x]&&to[i]!=son[x])
            {
                f[to[i]]=now;
                now+=mx[to[i]]+1;
                g[to[i]]=now+mx[to[i]]+1;
                now+=mx[to[i]]*2+2;
                dfs2(to[i]);
                for(int j=mx[to[i]];j>=0;j--)
                {
                    if(j)
                    {
                        ans+=f[x][j-1]*g[to[i]][j];
                    }
                    ans+=g[x][j+1]*f[to[i]][j];
                    g[x][j+1]+=f[to[i]][j]*f[x][j+1];
                }
                for(int j=0;j<=mx[to[i]];j++)
                {
                    if(j)
                    {
                        g[x][j-1]+=g[to[i]][j];
                    }
                    f[x][j+1]+=f[to[i]][j];
                }
            }
        }
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            add(x,y);
            add(y,x);
        }
        dfs(1);
        f[1]=now;
        now+=mx[1]+1;
        g[1]=now+mx[1]+1;
        now+=mx[1]*2+2;
        dfs2(1);
        printf("%lld",ans);
    }
  • 相关阅读:
    发布在Xilinx 嵌入式系统中文论坛的旧帖子
    【分享】MPSoC,XEN虚拟机运行裸核应用程序(baremetal, standalone)的中断延迟
    【分享】 在Vivado里关闭R5/GPU,降低Xilinx MPSoC的功耗
    python实现单链表
    Flink 作业问题分析和调优实践
    flink 有状态 udf引发的大坑一
    flink checkpoint 在 window 操作下 全局配置失效的问题
    python实现十大经典排序算法
    Flink on yarn 常见错误
    搭建Spark所遇过的坑
  • 原文地址:https://www.cnblogs.com/Khada-Jhin/p/9581559.html
Copyright © 2020-2023  润新知