zoukankan      html  css  js  c++  java
  • 【BZOJ4675】点对游戏 树分治+期望

    【BZOJ4675】点对游戏

    Description

    桑尼、露娜和斯塔在玩点对游戏,这个游戏在一棵节点数为n的树上进行。
    桑尼、露娜和斯塔三人轮流从树上所有未被占有的节点中选取一点,归为己有,轮流顺序为桑尼、露娜、斯塔、桑尼、露娜……。该选取过程直到树上所有点都被选取后结束。
    选完点后便可计算每人的得分。点对游戏中有m个幸运数,在某人占据的节点中,每有一对点的距离为某个幸运数,就得到一分。(树上两点之间的距离定义为两点之间的简单路径的边数)
    你的任务是,假设桑尼、露娜和斯塔每次选取时,都是从未被占有的节点中等概率选取一点,计算每人的期望得分。

    Input

    第一行两个整数n、m,分别表示树的节点数和幸运数的数目。
    第二行m个互异正整数,表示m个幸运数。
    以下n-1行,每行两个整数u、v,表示节点u和节点v之间有边。节点从1
    到n编号。
    3 <= n <= 50000, m <= 10,幸运数大小 <= n

    Output

    三行实数,分别表示桑尼、露娜和斯塔的期望得分,保留两位小数。

    Sample Input

    5 2
    1 3
    1 2
    1 5
    2 3
    2 4

    Sample Output

    0.60
    0.60
    0.00

    题解:本题的做法比较神。

    先用点分治统计出所有幸运点对的个数,然后分别统计每个人都选出了多少个点对,用选出的点对数*幸运点对数/总点对数 即是答案。

     

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    const int maxn=50010;
    int sum;
    int n,m,cnt,rt,mn,tot;
    int to[maxn<<1],next[maxn<<1],head[maxn],dep[maxn],vis[maxn],siz[maxn],luck[20],f[maxn],g[maxn],md[maxn];
    double ans[maxn],s[maxn];
    void getrt(int x,int fa)
    {
    	siz[x]=1;
    	int tmp=0;
    	for(int i=head[x];i!=-1;i=next[i])	if(to[i]!=fa&&!vis[to[i]])
    		getrt(to[i],x),siz[x]+=siz[to[i]],tmp=max(tmp,siz[to[i]]);
    	tmp=max(tmp,tot-siz[x]);
    	if(tmp<mn)	mn=tmp,rt=x;
    }
    void getmd(int x,int fa,int dep)
    {
    	siz[x]=1,md[x]=0;
    	for(int i=head[x];i!=-1;i=next[i])	if(to[i]!=fa&&!vis[to[i]])
    		getmd(to[i],x,dep+1),siz[x]+=siz[to[i]],md[x]=max(md[x],md[to[i]]+1);
    }
    void getdep(int x,int fa,int dep)
    {
    	g[dep]++;
    	for(int i=1;i<=m;i++)	if(dep<=luck[i])	sum+=f[luck[i]-dep];
    	for(int i=head[x];i!=-1;i=next[i])	if(to[i]!=fa&&!vis[to[i]])	getdep(to[i],x,dep+1);
    }
    void dfs(int x)
    {
    	vis[x]=1;
    	getmd(x,0,0);
    	f[0]=1;
    	for(int i=head[x];i!=-1;i=next[i])	if(!vis[to[i]])
    	{
    		getdep(to[i],x,1);
    		for(int j=0;j<=md[to[i]]+1;j++)	f[j]+=g[j],g[j]=0;
    	}
    	memset(f,0,sizeof(f[0])*(md[x]+2));
    	for(int i=head[x];i!=-1;i=next[i])	if(!vis[to[i]])	tot=siz[to[i]],mn=1<<30,getrt(to[i],x),dfs(rt);
    }
    inline void add(int a,int b)
    {
    	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
    }
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    int main()
    {
    	//freopen("game0.in","r",stdin);
    	//freopen("game.out","w",stdout);
    	n=rd(),m=rd();
    	int i,a,b;
    	for(i=1;i<=m;i++)	luck[i]=rd();
    	memset(head,-1,sizeof(head));
    	for(i=1;i<n;i++)	a=rd(),b=rd(),add(a,b),add(b,a);
    	tot=n,mn=1<<30,getrt(1,0),dfs(rt);
    	a=(n+2)/3,printf("%.2lf
    ",1.0*a*(a-1)*sum/(1.0*n*(n-1)));
    	a=(n+1)/3,printf("%.2lf
    ",1.0*a*(a-1)*sum/(1.0*n*(n-1)));
    	a=n/3,printf("%.2lf
    ",1.0*a*(a-1)*sum/(1.0*n*(n-1)));
    	return 0;
    }

     

  • 相关阅读:
    对计算机科学与技术专业的认识及未来的规划
    秋季学期学习总结
    自我介绍
    Leetcode每日一题 83. 删除排序链表中的重复元素
    Leetcode每日一题 82. 删除排序链表中的重复元素 II
    Leetcode每日一题 456.132 模式
    Leetcode每日一题 341. 扁平化嵌套列表迭代器
    Leetcode每日一题 191. 位1的个数
    Leetcode每日一题 73. 矩阵置零
    Leetcode每日一题 150. 逆波兰表达式求值
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7669861.html
Copyright © 2011-2022 走看看