1. 程式人生 > >【BZOJ4675】點對遊戲 樹分治+期望

【BZOJ4675】點對遊戲 樹分治+期望

三人 hellip 小數 tro 統計 int 多少 microsoft 題解

【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\n",1.0*a*(a-1)*sum/(1.0*n*(n-1)));
	a=(n+1)/3,printf("%.2lf\n",1.0*a*(a-1)*sum/(1.0*n*(n-1)));
	a=n/3,printf("%.2lf\n",1.0*a*(a-1)*sum/(1.0*n*(n-1)));
	return 0;
}

【BZOJ4675】點對遊戲 樹分治+期望