[2016北京集训试题7]thr-[树形dp+树链剖分+启发式合并]

Description

Solution

神仙操作orz。

首先看数据范围,显然不可能是O(n2)的。(即绝对不是枚举那么简单的),我们考虑dp。

定义f(x,k)为以x为根的子树中与x距离为k的节点数;g(x,k)为在以x为根的子树中选择两个点,使得另一个点应在x子树外且离x距离为k的方案数(或者距离为0)。但是这样子暴力转移怕是会崩em,考虑优化。

这里的树是棵静态树,考虑树链剖分,点分治之类的思想。

最后,由于很多时候它的复杂度和树的高度有关,考虑长链剖分。

转移的话,暴力枚举所有轻链(啊我可能说的不是很清楚,类似启发式合并的想法吧)。

那么该点对应的长链怎么办呢?类似树剖的重儿子,我们定义节点x的长儿子为son[x],它是只有一个的。

而假如该点只有一个儿子,f(x,k)=f(son[x],k-1),g(x,k)=g(son[x],k+1)。

还是和树剖一样的思想,我们把同一条长链的信息存在一起,例如把f(son[x],k-1)的信息与f(x,k)存在同一个地址下。(我们能这么做是因为son[x]的信息在被x更新完后它就没用了啊)。g的话同理。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=50010;
int mxdep[N],top[N],fa[N],dep[N],son[N],cnt_f[N],cnt_g[N],cntf=0,cntg=0;
ll F[N],G[N<<1];
int getlen(int x){return mxdep[x]-dep[x]+1;}
ll &f(int x,int y){return F[cnt_f[top[x]]+dep[x]-dep[top[x]]+y];}
ll &g(int x,int y){return G[cnt_g[top[x]]-dep[x]+dep[top[x]]+y];}
struct _pas{int y,nxt;}_g[N<<1];int h[N],tot=0;
void dfs1(int x,int f)
{
    fa[x]=f;dep[x]=dep[f]+1;mxdep[x]=dep[x];son[x]=0;
    for (int i=h[x];i;i=_g[i].nxt)
    if (_g[i].y!=f){
        dfs1(_g[i].y,x);
        mxdep[x]=max(mxdep[x],mxdep[_g[i].y]);
        if (mxdep[son[x]]<mxdep[_g[i].y]) son[x]=_g[i].y;
    }
}
void dfs2(int x)
{
    top[x]=son[fa[x]]==x?top[fa[x]]:x;
    if (top[x]==x)
    {
        int d=getlen(x);
        cnt_f[x]=cntf;cnt_g[x]=cntg+d;
        cntf+=d;cntg+=2*d;
    }
    if (son[x]) dfs2(son[x]);
    for (int i=h[x];i;i=_g[i].nxt)
    if (_g[i].y!=son[x]&&_g[i].y!=fa[x]) dfs2(_g[i].y);
}
ll ans;
void dp(int u)
{
    f(u,0)=1;
    if (son[u]) dp(son[u]),ans+=g(u,0);
    for (int i=h[u];i;i=_g[i].nxt)
    if (_g[i].y!=son[u]&&_g[i].y!=fa[u]) 
    {
        int v=_g[i].y;dp(v);int len=getlen(v);
        for (int i=1;i<=len;i++) ans+=g(u,i)*f(v,i-1);
        for (int i=1;i<len;i++) ans+=g(v,i)*f(u,i-1);
        for (int i=1;i<=len;i++) g(u,i)+=f(u,i)*f(v,i-1);
        for (int i=1;i<=len-2;i++) g(u,i)+=g(v,i+1);
        for (int i=1;i<=len;i++) f(u,i)+=f(v,i-1);
    }
}
void _clear()
{
    tot=cntf=cntg=0;
    memset(h,0,sizeof(h));
    memset(F,0,sizeof(F));
    memset(G,0,sizeof(G));
}
int n,x,y;
int main()
{
    while (1)
    {
        scanf("%d",&n);
        if (!n) return 0;
        _clear();
        for (int i=1;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            _g[++tot]=_pas{y,h[x]};h[x]=tot;
            _g[++tot]=_pas{x,h[y]};h[y]=tot;
        }
        ans=0;
        dfs1(1,0);dfs2(1);dp(1);
        printf("%lld
",ans);       
    }
}
原文地址:https://www.cnblogs.com/coco-night/p/9615465.html