2014多校1(1003)hdu4863

Centroid of a Tree

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 194    Accepted Submission(s): 81


Problem Description
Given a tree T with N vertices, your task is calculating the number of the connected sub-tree of T, which has the same centroid as T.
In order to define the centroid, some integer value will be associated to every vertex. Let's consider the vertex k. If we remove the vertex k from the tree (along with its adjacent edges), the remaining graph will have only N-1 vertices and may be composed of more than one connected components. Each of these components is (obviously) a tree. The value associated to vertex k is the largest number of vertices contained by some connected component in the remaining graph, after the removal of vertex k. All the vertices for which the associated value is minimum are considered centroids.
A graph can have an arbitrary number of centroids. However,it can be proved that for trees, there are only two possibilities:
1. The tree has precisely one centroid.
2. The tree has precisely two centroids. In this case, the two centroids are adjacent.
Note that in case 2, we just consider that T has two centroids, you should only count the sub-tree which has the two same centroids as T.
 

Input
The first line of the date is an integer T, which is the number of the text cases.
Then T cases follow each case starts of a number N descript above.
Then N-1 lines follow, each line contains two integers x and y, which means that there is a edge between x and y in tree T, you can assume that the index of T is from 1 to N.
1 <= T <= 50, 1 <= N <= 200,
 

Output
For each case, output the case number first, then output the number of the connected sub-tree which has the same centroid as T.
Give your answer modulo 10007.
 

Sample Input
       
       
5 1 2 1 2 3 1 2 1 3 4 1 2 1 3 1 4 5 1 2 1 3 1 4 4 5
 

Sample Output
       
       
Case 1: 1 Case 2: 1 Case 3: 2 Case 4: 5 Case 5: 6
很单纯的树形DP,大体思路是用dp[i][j]表示以i为根的子树节点数为j的方案数,先用一遍dfs找出树的重心,然后分重心一个和两个这两种情况讨论。

重心如果是两个则很简单,直接将两边相同节点的方案数相乘,然后再累加起来就好了

如果只有一个重心,那么可以从反面情况来求解,即先可以算出不满足的方案数,以该重心为根,枚举最大分支,剩下的分支的节点数中和要小于最大分支的节点数,这个直接用背包处理处理即可,最后再用所有情况减去反面的情况就是答案了。


#include 
   
   
    
    //树形dp,好题
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
using namespace std;
#define INF ((1<<31)-1)
#define mod 10007

int n,d;
int head[210];
int next[210<<1];
int edge[210<<1];
int num[210];
int cnum[210];
int center[2];
__int64 dp[210][210];
__int64 dp1[210];
int maxii;
int hehe[210];

int maxi(int x,int y)
{
    return x>y?x:y;
}

void add(int u,int v)
{
    edge[d]=v;
    next[d]=head[u];
    head[u]=d++;
}

void dfs(int u,int pre)
{
    int i;
    num[u]=1;
    cnum[u]=0;
    for(i=head[u];i!=-1;i=next[i])if(edge[i]!=pre){
    dfs(edge[i],u);
    num[u]+=num[edge[i]];
    if(num[edge[i]]>cnum[u])cnum[u]=num[edge[i]];
    }
}

void dfs1(int u,int pre)
{
    int i;
    int temp=maxi(n-num[u],cnum[u]);
    if(temp
       
       
         1;j--) for(jj=1;jj 
        
          <=num[edge[i]];jj++)dp[u][j]=(dp[u][j]+dp[edge[i]][jj]*dp[u][j-jj]%mod)%mod; } } int dd; __int64 ff[210]; __int64 anss; void cnt() { int i,j; __int64 total=1; __int64 temp; dd=0; for(i=head[center[0]];i!=-1;i=next[i]){ hehe[dd++]=edge[i]; temp=0; for(j=0;j<=num[edge[i]];j++)temp=(temp+dp[edge[i]][j])%mod; total=(total*temp)%mod; } anss=0; for(i=0;i 
         
           =1;cc--) for(int xx=1;xx<=num[hehe[j]]&&xx<=cc;xx++) { ff[cc]=(ff[cc]+ff[cc-xx]*dp[hehe[j]][xx]%mod)%mod; } } for(j=0;j<=num[hehe[i]]-1;j++){ if(j>0)ff[j]=(ff[j]+ff[j-1])%mod; anss=(anss+(ff[j]*dp[hehe[i]][j+1])%mod)%mod; } } printf("%I64d\n",((total-anss)%mod+mod)%mod); } int ddd; void solve() { int i,j; dfs(1,0); center[1]=0; maxii=INF; dfs1(1,0); memset(dp,0,sizeof(dp)); printf("Case %d: ",++ddd); if(center[1]==0) { dfs2(center[0],0); cnt(); } else { dfs2(center[0],center[1]); dfs2(center[1],center[0]); __int64 total=0; for(i=1;i<=num[center[0]]&&i<=num[center[1]];i++) { total=(total+(dp[center[0]][i]*dp[center[1]][i])%mod)%mod; } printf("%I64d\n",total); } } int main() { int t,i,j,u,v; scanf("%d",&t); ddd=0; while(t--) { scanf("%d",&n); d=0; memset(head,-1,sizeof(head)); for(i=1;i 
           
          
         
       
      
      
     
     
    
    
   
   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值