其实这题是个乱搞题...
给定一棵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;
}