FZU 2142 Center of a Tree (树dp+组合计数)

题意:给出n个点的树,问它的子树有多少和它有相同的中心。树中心是指使得距离树最远的点最近的点,中心有可能有1个或2个。

思路:感觉这题好难做。。。首先是要求中心,这个还是很好做的,直接树dp就能解决,然后要分中心个数是1个或2个这两种情况。首先求一下dp[i][j],这个表示结点i的子树中,距离i不超过j的方案数。这样对于结点i,想要求出长度为k的子树的个数就可以用dp[i][k]-dp[i][k-1]求出。接下来,如果中心的个数为2,那么很简单,只要令这两个中心的子树最长的距离相等就可以,枚举一下距离就行了。比较麻烦的是中心个数为1个的情况,这种情况要求保证最长距离为k时,选出的子树中,至少有两个中心结点的儿子节点并且它们的最长距离为k。这个想了挺长时间没什么好办法,只好用dp搞一搞,dp2[i][0],dp[i][1],dp[i][2]分别表示以前i个儿子结点构成的子树中,长度为k的子树有0个,1个,大于等于2个,那么可以写成状态转移方程:

dp2[j][0]=dp2[j-1][0]*(1+dp[v][i-1]);
dp2[j][1]=dp2[j-1][1]*(1+dp[v][i-1])+dp2[j-1][0]*(dp[v][i]-dp[v][i-1]);
dp2[j][2]=dp2[j-1][2]*(1+dp[v][i-1])+(dp2[j-1][1]+dp2[j-1][2])*(dp[v][i]-dp[v][i-1]);

最后加上答案就行了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=1000+10;
const int mod=10086;
struct Edge
{
    int v,next;
    Edge(){}
    Edge(int v,int next):v(v),next(next){}
}edges[maxn<<1];
int head[maxn],dp[maxn][maxn],son[maxn],dp2[maxn][3],nEdge;
int res[2],d[maxn],S[maxn],p[maxn],minlen;
void AddEdges(int u,int v)
{
    edges[++nEdge]=Edge(v,head[u]);
    head[u]=nEdge;
    edges[++nEdge]=Edge(u,head[v]);
    head[v]=nEdge;
}
void dfs(int u,int fa)
{
    dp[u][0]=dp[u][1]=0;
    son[u]=0;
    for(int k=head[u];k!=-1;k=edges[k].next)
    {
        int v=edges[k].v;
        if(v==fa) continue;
        dfs(v,u);
        if(dp[v][0]+1>dp[u][0])
        {
            dp[u][1]=dp[u][0];
            dp[u][0]=dp[v][0]+1;
            son[u]=v;
        }
        else if(dp[v][0]+1>dp[u][1])
            dp[u][1]=dp[v][0]+1;
    }
}
void dfs2(int u,int fa,int maxlen)
{
    int tmp=max(maxlen,dp[u][0]);
    if(tmp<minlen)
    {
        minlen=tmp;
        res[0]=u;
        res[1]=-1;
    }
    else if(tmp==minlen)
        res[1]=u;
    for(int k=head[u];k!=-1;k=edges[k].next)
    {
        int v=edges[k].v;
        if(v==fa) continue;
        tmp=(son[u]==v)?dp[u][1]+1:dp[u][0]+1;
        dfs2(v,u,max(maxlen+1,tmp));
    }
}
void init()
{
    memset(head,0xff,sizeof(head));
    memset(son,0,sizeof(son));
    memset(d,0,sizeof(d));
    nEdge=res[0]=res[1]=-1;
    minlen=inf;
}
void DFS(int u,int fa)
{
    for(int i=0;i<maxn;++i) dp[u][i]=1;
    for(int k=head[u];k!=-1;k=edges[k].next)
    {
        int v=edges[k].v;
        if(v==fa) continue;
        DFS(v,u);
        for(int i=1;i<maxn;++i)
            dp[u][i]=(dp[u][i]+dp[u][i]*dp[v][i-1])%mod;
    }
}
int solve(int n)
{
    dfs(1,-1);
    dfs2(1,-1,0);
    int type=(res[1]!=-1);
    DFS(res[0],res[1]);
    if(type) DFS(res[1],res[0]);
    int ans=1,size;
    if(type==0)
    {
        size=0;
        for(int k=head[res[0]];k!=-1;k=edges[k].next)
            S[++size]=edges[k].v;
        if(size>1) ans=(ans+p[size]-size-1)%mod;
        for(int i=1;i<maxn;++i)
        {
            dp2[0][0]=1;dp2[0][1]=dp2[0][2]=0;
            for(int j=1;j<=size;++j)
            {
                dp2[j][0]=dp2[j-1][0]*(1+dp[S[j]][i-1])%mod;
                dp2[j][1]=dp2[j-1][1]*(1+dp[S[j]][i-1])%mod+dp2[j-1][0]*(dp[S[j]][i]-dp[S[j]][i-1])%mod;
                dp2[j][2]=dp2[j-1][2]*(1+dp[S[j]][i-1])%mod+(dp2[j-1][1]+dp2[j-1][2])*(dp[S[j]][i]-dp[S[j]][i-1])%mod;
                dp2[j][1]%=mod;dp2[j][2]%=mod;
            }
            ans=(ans+dp2[size][2])%mod;
        }
    }
    else
    {
        int tmp1,tmp2;
        for(int i=1;i<maxn;++i)
        {
            tmp1=dp[res[0]][i]-dp[res[0]][i-1];
            tmp2=dp[res[1]][i]-dp[res[1]][i-1];
            ans=(ans+tmp1*tmp2)%mod;
        }
    }
    return (ans%mod+mod)%mod;
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    p[0]=1;
    for(int i=1;i<maxn;++i) p[i]=(p[i-1]<<1)%mod;
    int n,t,tcase=0;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        init();
        int u,v;
        for(int i=1;i<n;++i)
        {
            scanf("%d%d",&u,&v);
            AddEdges(u,v);
        }
        printf("Case %d: %d\n",++tcase,solve(n));
    }
    return 0;
}



你可能感兴趣的:(dp)