SPOJ AMR12K The Loyalty of the Orcs 解题报告

题目

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


你可能感兴趣的:(SPOJ AMR12K The Loyalty of the Orcs 解题报告)