BZOJ4675: 点对游戏

BZOJ4675: 点对游戏

https://lydsy.com/JudgeOnline/problem.php?id=4675

分析:

  • 对于一个人,如果选了m个点,答案显然是点对数量乘(inom{n-2}{m-2})(inom{n}{m})
  • 点分治统计点对数量即可。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef double f2;
#define N 100050
int head[N],to[N<<1],nxt[N<<1],cnt,n;
int m,lk[N],tot,a[N],la,b[N],lb,h[N];
int dep[N],f[N],siz[N],root,used[N];
f2 sum;
inline void add(int u,int v) {
	to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt;
}
f2 work(int n,int m) {
	return sum*m*(m-1)/(f2(n)*(n-1));
}
void get_root(int x,int y) {
	int i;
	siz[x]=1; f[x]=0;
	for(i=head[x];i;i=nxt[i]) if(to[i]!=y&&!used[to[i]]) {
		get_root(to[i],x);
		siz[x]+=siz[to[i]];
		f[x]=max(f[x],siz[to[i]]);
	}
	f[x]=max(f[x],tot-siz[x]);
	if(f[x]<f[root]) root=x;
}
void get_dep(int x,int y) {
	int i;
	dep[x]=dep[y]+1;
	a[++la]=x; b[++lb]=x;
	for(i=1;i<=m;i++) {
		if(lk[i]-dep[x]>=0) {
			sum+=h[lk[i]-dep[x]];
		}
	}
	for(i=head[x];i;i=nxt[i]) if(to[i]!=y&&!used[to[i]]) {
		get_dep(to[i],x);
	}
}
void solve(int x) {
	used[x]=1;
	int i,j;
	la=lb=0;
	a[++la]=0;
	dep[x]=0;
	h[0]=1;
	for(i=head[x];i;i=nxt[i]) if(!used[to[i]]) {
		lb=0;
		get_dep(to[i],x);
		for(j=1;j<=lb;j++) h[dep[b[j]]]++;
	}
	for(i=1;i<=la;i++) h[dep[a[i]]]=0;
	for(i=head[x];i;i=nxt[i]) if(!used[to[i]]) {
		tot=siz[to[i]]; root=0;
		get_root(to[i],0);
		solve(root);
	}
}
int main() {
	// freopen("game.in","r",stdin);
	// freopen("game.out","w",stdout);
	scanf("%d%d",&n,&m);
	int i;
	for(i=1;i<=m;i++) scanf("%d",&lk[i]);
	int x,y;
	for(i=1;i<n;i++) {
		scanf("%d%d",&x,&y);
		add(x,y), add(y,x);
	}
	tot=n;
	root=0; f[0]=1<<30;
	get_root(1,0);
	solve(root);
	printf("%.2f
%.2f
%.2f
",work(n,n/3+(n%3>0)),work(n,n/3+(n%3>1)),work(n,n/3));
}

原文地址:https://www.cnblogs.com/suika/p/10051637.html