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