[HDOJ 4900] NO ACM NO LIFE [树的直径]

69 篇文章 0 订阅
23 篇文章 0 订阅

其实这题是个乱搞题...

给定一棵n个节点的树,每个节点上都有标签,问从中选取k个节点组成最小的连通子图(也是一棵树),它的直径上的点的不同的标签个数的期望。如果多条直径一样,则选取字典许最小的(a,b)对作为直径。

首先dfs求出任意两点之间的距离,和这条路上不同的标签个数。

然后枚举直径,计算生成的树的直径为这条直径的概率。求和即可。

k=0和k=1的时候要特判..但是标程没写特判..所以不加特判就能AC...加了特判反而WA了..

#include <cstdio>
#include <cstring>
#include <map>

using namespace std;

struct Node {
	int fe,v;
	bool visited;
};
struct Edge {
	int t,ne;
};

Node a[301];
Edge b[600];
double c[301][301]={0};
int dis[301][301];
int lable[301][301];
int n,kk,bp,s;
map<int,int>numLab;

void putedge(int x,int y) {
	b[bp].t=y;
	b[bp].ne=a[x].fe;
	a[x].fe=bp++;
}
void dfs(int i,int h) {
	a[i].visited=true;
	numLab[a[i].v]++;
	lable[s][i]=numLab.size();
	dis[s][i]=h;
	for (int j=a[i].fe;~j;j=b[j].ne) {
		if (!a[b[j].t].visited) {
			dfs(b[j].t,h+1);
		}
	}
	if (numLab[a[i].v]!=1) numLab[a[i].v]--;
	else numLab.erase(a[i].v);
	a[i].visited=false;
}

int main() {
	int t,i,j,k;
	c[0][0]=1;
	for (i=1;i<=300;i++) {
		c[i][0]=c[i][i]=1;
		for (j=1;j<i;j++)
			c[i][j]=c[i-1][j-1]+c[i-1][j];
	}
	scanf("%d",&t);
	while (t--) {
		scanf("%d%d",&n,&kk);
		for (i=1;i<=n;i++) {
			a[i].fe=-1;
			a[i].visited=false;
		}
		bp=0;
		for (i=1;i<n;i++) {
			int x,y;
			scanf("%d%d",&x,&y);
			putedge(x,y);
			putedge(y,x);
		}
		for (i=1;i<=n;i++) scanf("%d",&a[i].v);
		for (i=1;i<=n;i++) {
			s=i;
			numLab.clear();
			dfs(i,0);
		}
		double ans=0;
		if (kk<=0) ans=0;
		else if (kk==1) ans=1;
		else {
			for (i=1;i<=n;i++)
				for (j=i+1;j<=n;j++) {
					int num=0;
					int v=dis[i][j];
					for (k=1;k<=n;k++) {
						if (k!=i&&k!=j
								&&(dis[i][k]<v||(dis[i][k]==v&&k>j))
								&&(dis[j][k]<v||(dis[j][k]==v&&k>i)))
							num++;
					}
					ans+=c[num][kk-2]/c[n][kk]*lable[i][j];
				}
		}
		printf("%.9lf\n",ans);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值