Summer Training 06 - Amritapuri 2012 总结
题意:
给世界上最纯正的英语跪了,我真的读了好多好多遍……简单来说就是有一棵树,有一些节点已经死了。随机一个1~N的序列,按序列检查这个节点是否已死并计数加1.有一种优化的方法,就是如果在检查这个节点之前已经发现它有祖先死了,那就不+1了。求优化的方法省下来的检查次数的期望。
题解:
显然不优化的话所有点都检查,那么计数为N。如果优化的话,要使得某个节点检查的话,它所有死了的祖先都得放在它后面检查。设某节点有K个祖先死了,那放在它后面的排列是K!,剩下的(n-k-1)个点就随便放,方案数是n!/(k+1)!,总的方案数是n!,所以这个检点检查的概率是1/(k+1)。全部节点的概率加起来用n一减就是期望。
//Time:370ms
//Memory:10240KB
//Length:1060B
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <set>
#define MAXN 200010
using namespace std;
int he[MAXN],to[MAXN],nex[MAXN],top;
bool vi[MAXN];
double ans;
void add(int u,int v)
{
to[top]=v;
nex[top]=he[u];
he[u]=top++;
}
void dfs(int h,int cnt,int fa)
{
ans+=1.0/cnt;
for(int i=he[h];i!=-1;i=nex[i])
if(fa!=to[i])
dfs(to[i],cnt+vi[h],h);
}
int main()
{
//freopen("/home/moor/Code/input","r",stdin);
int ncase,n,m;
scanf("%d",&ncase);
while(ncase--)
{
ans=0;
scanf("%d",&n);
memset(he,-1,sizeof(he));
memset(vi,0,sizeof(vi));
top=0;
for(int i=1;i<n;++i)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
scanf("%d",&m);
for(int i=0;i<m;++i)
{
int tmp;
scanf("%d",&tmp);
vi[tmp]=1;
}
dfs(1,1,-1);
printf("%.8f\n",n-ans);
}
return 0;
}