[HDOJ 4863] Centroid of a Tree [树形DP]

69 篇文章 0 订阅
3 篇文章 0 订阅

给定一颗无根树,问有多少棵它的子树具有和他相同的重心?如果一棵树有两个根,那么子树也同样要有那两个重心。

首先找到树的重心,重心上连的每个节点所带领的子树的节点个数的两倍都不会超过n,如果是两个重心的话,两边的总节点个数应该是相同的

用树形dp求出每个节点所带领的子树的节点个数为i的时候的方案数,最后枚举总共的节点数,用dp求出总结点数为n的时候的方案数,相加得到结果

数据略水...貌似数据是随机生成的..所以没有出现有两个重心的树...

时限挺紧的..200^3的复杂度竟然T了..加了常数优化以后才A...

#include <cstdio>
#include <cstring>

using namespace std;

const int mod=10007;

struct Node {
	int dp[200];
	int fe,num;
	bool visited;
};
struct Edge {
	int t,ne;
};

int n,center1,center2,p,maxsize;
Node a[201];
Edge b[400];

void putedge(int x,int y) {
	b[p].t=y;
	b[p].ne=a[x].fe;
	a[x].fe=p++;
}
void getnum(int i) {
	a[i].visited=true;
	int maxn=0;
	a[i].num=1;
	for (int j=a[i].fe;j!=-1;j=b[j].ne) {
		if (!a[b[j].t].visited) {
			getnum(b[j].t);
			a[i].num+=a[b[j].t].num;
			if (maxn<a[b[j].t].num) maxn=a[b[j].t].num;
		}
	}
	if (maxn<n-a[i].num) maxn=n-a[i].num;
	if (maxsize>maxn) {
		maxsize=maxn;
		center1=i;
		center2=-1;
		maxsize=maxn;
	} else if (maxsize==maxn) {
		center2=i;
	}
	a[i].visited=false;
}

int tmpdp[200];

void getdp(int i) {
	a[i].visited=true;
	a[i].num=1;
	a[i].dp[1]=1;
	for (int j=a[i].fe;j!=-1;j=b[j].ne) {
		if (!a[b[j].t].visited) {
			getdp(b[j].t);
			if (i!=center1&&i!=center2) {
				memcpy(tmpdp,a[i].dp,sizeof(tmpdp));
				memset(a[i].dp,0,sizeof(tmpdp));
				for (int p=0;p<=a[i].num;p++) {
					for (int q=0;q<=a[b[j].t].num;q++) {
						a[i].dp[p+q]=(a[i].dp[p+q]+tmpdp[p]*a[b[j].t].dp[q])%mod;
					}
				}
			}
			a[i].num+=a[b[j].t].num;
		}
	}
	a[i].dp[0]=1;
	a[i].visited=false;
}

int getans(int center,int nn,int n) {
	//printf("%d %d %d\n",center,nn,n);
	memset(a[center].dp,0,sizeof(a[center].dp));
	a[center].dp[0]=1;
	for (int j=a[center].fe;j!=-1;j=b[j].ne) {
		if (!a[b[j].t].visited) {
			memcpy(tmpdp,a[center].dp,sizeof(tmpdp));
			memset(a[center].dp,0,sizeof(tmpdp));
			int num=a[b[j].t].num;
			for (int p=0;p<=nn;p++) {
				int *centerpq=&a[center].dp[p];
				int tmp=tmpdp[p];
				int *dpq=&a[b[j].t].dp[0];
				for (int q=0;p+q<=nn&&q+q<n&&q<=num;q++,centerpq++,dpq++) {
					*centerpq=(*centerpq+tmp*(*dpq))%mod;
				}
			}
		}
	}
	//printf("%d\n",a[center].dp[nn]);
	return a[center].dp[nn];
}
void print() {
	for (int i=1;i<=n;i++) {
		for (int j=0;j<n;j++) 
			printf("%d ",a[i].dp[j]);
		printf("\n");
	}
}

int main() {
	int t,tt,x,y,i;
	int ans;
	scanf("%d",&t);
	for (tt=1;tt<=t;tt++) {
		scanf("%d",&n);
		for (i=1;i<=n;i++) {
			memset(a[i].dp,0,sizeof(a[i].dp));
			a[i].fe=-1;
			a[i].visited=false;
			a[i].num=0;
		}
		p=0;
		for (i=1;i<n;i++) {
			scanf("%d%d",&x,&y);
			putedge(x,y);
			putedge(y,x);
		}
		maxsize=n;
		center1=center2=-1;
		getnum(1);
		ans=0;
		//printf("%d %d\n",center1,center2);
		if (center2!=-1) {
			a[center2].visited=true;
			getdp(center1);
			a[center1].visited=true;
			getdp(center2);
			a[center1].visited=a[center2].visited=true;
			for (i=0;i+i+2<=n;i++) {
				ans=(ans+getans(center1,i,i+i+2)*getans(center2,i,i+i+2))%mod;
			}
		} else {
			getdp(center1);
			for (i=0;i<n;i++) ans=(ans+getans(center1,i,i+1))%mod;
		}
		//print();
		printf("Case %d: %d\n",tt,ans);
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值