题目
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; }