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);
}
posted @ 2018-09-06 20:25  The_Virtuoso  阅读(617)  评论(0编辑  收藏  举报