Codeforces 23 E Tree 树形dp+高精

题目大意: 给你一棵树,让你切断一些边,使得剩下的每个连通块的点的个数的乘积最大,输出这个乘积。



题目分析: 由于题目要求各个连通块点的乘积,设状态dp[i][j]表示i作为一个连通块时其点个数为j时候的值。每次枚举与当前节点联通的其他节点所已经扩展的总结点数量。则dp[u][i+j]=max(dp[u][i]*dp[v][j]) {其中u为当前节点,v为u的子节点,i,j分别枚举u和v的联通块的点的个数。}

再加上高精压四位处理就ac了。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
#include<cmath>
#include<cctype>
#include<cassert>
#include<climits>
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Rep(i,n) for(int i=0;i<n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define Forp(x) for(int p=pre[x];p;p=next[p])
#define RepD(i,n) for(int i=n;i>=0;i--)
#define MEM(a) memset(a,0,sizeof(a))
#define MEMI(a) memset(a,127,sizeof(a))
#define MEMi(a) memset(a,128,sizeof(a))
#define INF (2139062143)
#define phiF (1000000006)
#define MAXN (1000000+10)
typedef long long LL;
const int mod = 10000;
int num[2005],first[2005],n,u,v,tot;

struct info{
	int to,next;
}e[2005];

void add(int x,int y){
	tot++;
	e[tot].to=y;
	e[tot].next=first[x];
	first[x]=tot;
}

struct tar{
	int a[80],n;
	tar(){};
	tar(int x) {
    MEM(a);
    n = 1, a[0] = x;
    }
    
    void init(int x) { n = 1, a[0] = x; }
	bool operator < (const tar &x) {
    if (n < x.n)
      return 1;
    else if (n > x.n)
      return 0;
    RepD(i,n-1)
      if (a[i] < x.a[i])
        return 1;
      else if (a[i] > x.a[i])
        return 0;
    return 0;
    }
    
    tar operator * (const tar &x) {
      tar t(0);
      t.n = x.n + n - 1;
      Rep(i,n)
     	 Rep(j,x.n) t.a[i + j] += a[i] * x.a[j];
      Rep(i,t.n) {
      	t.a[i + 1] += t.a[i] / mod;
      	t.a[i] %= mod;
      }
    if (t.a[t.n]) t.n++;
    return t;
  }
  tar operator + (const tar x) {
    tar t(0);
    t.n = max(x.n, n);
    Rep(i,t.n) {
      t.a[i] = t.a[i] + x.a[i] + a[i];
      t.a[i + 1] += t.a[i] / mod;
      t.a[i] %= mod;
    }
    if (t.a[t.n]) ++t.n;
    return t;
  }
    void print() {
    printf("%d", a[n - 1]);
    RepD(i,n-2) printf("%04d", a[i]);
    puts(" ");
    }
    
}dp[705][705];

void dfs(int u,int fa){
	num[u]=1;
	dp[u][0]=(0);
	dp[u][1]=(1);
	for (int p=first[u];p;p=e[p].next){
		int v=e[p].to;
		if (v==fa) continue ;
		dfs(v,u);
		RepD(i,num[u])
		 RepD(j,num[v]){
		 	 tar tmp=dp[u][i]*dp[v][j];
		 	if (dp[u][i+j]<tmp) dp[u][i+j]=tmp;
		 }
		num[u]+=num[v];
	}
	For (i,num[u]){
		tar tmp(i);
		tmp=tmp*dp[u][i];
		if (dp[u][0]<tmp) dp[u][0]=tmp;
	}
}


int main(){
	scanf("%d",&n);
	For (i,n-1){
		scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	
	dfs(1,0);
	dp[1][0].print();
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值