BZOJ4675 点对游戏
题目大意
给定一棵$N$个点的树$(N\leq 5\times 10^4)$,三个人轮流选点,每次等概率选一个之前没有被选过的点。
定义$M$个距离(简单路径上的边数)的点对$(M\leq 10)$是幸运的,求每个人分别获得的幸运点对数量的期望。
题解
首先不难发现每个人选的点是独立的且数量是确定的
假设选$k$个点,考虑每个点$x$对答案的贡献。设$P$表示$x$被选中的概率$=\frac{k}{N}$
枚举$x$作为点对中前一个点,对答案的贡献是$\sum P\times (k-1)\frac{V}{N-1}$,其中$V$表示能与$x$组成幸运点对的数量。
化简之后得到$\frac{k(k-1)}{N(N-1)}\sum V$。
不难发现$\sum V$其实就是树上所有的有序幸运点对数量,自然而然的想到点分治,不过由于这里的边权之和路径边数有关,所以我们可以使用长链剖分在$O(NM)$的时间复杂度内解决。
假设无序幸运点对数量是$t$个,选出$k$个获得的幸运点对数量期望就是$\frac{2tk(k-1)}{N(N-1)}$。
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #define LL long long #define M 100020 using namespace std; int read(){ int nm=0,fh=1; int cw=getchar(); for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh; for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0'); return nm*fh; } int n,m,fs[M],nt[M<<1],to[M<<1],tmp,D[12],t1,t2,t3; int mxd[M],dep[M],dfn[M],mxs[M],cnt,F[M],tot,sum; double m1,m2,m3,ans; void link(int x,int y){nt[tmp]=fs[x],fs[x]=tmp,to[tmp++]=y;} void dfs1(int x,int last){ mxd[x]=0; for(int i=fs[x];i!=-1;i=nt[i]){ if(to[i]==last) continue; dfs1(to[i],x); if(mxd[to[i]]>=mxd[x]) mxd[x]=mxd[to[i]]+1,mxs[x]=to[i]; } } void dfs2(int x,int last){ dfn[x]=++cnt,F[dfn[x]]=1; if(mxs[x]) dfs2(mxs[x],x); for(int i=1;i<=m;i++) if(D[i]<=mxd[x]) tot+=F[dfn[x]+D[i]]; for(int i=fs[x];i!=-1;i=nt[i]){ int v=to[i]; if(v==last||v==mxs[x]) continue; dfs2(v,x); for(int k=0;k<=mxd[v];k++) for(int j=1;j<=m;j++) if(D[j]>k&&D[j]-k-1<=mxd[x]) tot+=F[dfn[v]+k]*F[dfn[x]+D[j]-1-k]; for(int k=0;k<=mxd[v];k++) F[dfn[x]+k+1]+=F[dfn[v]+k]; } } int main(){ n=read(),m=read(),memset(fs,-1,sizeof(fs)); for(int i=1;i<=m;i++) D[i]=read(); for(int i=1;i<n;i++){int x=read(),y=read();link(x,y),link(y,x);} t1=t2=t3=n/3,t1+=(n%3>0),t2+=(n%3>1); m1=(t1*(t1-1)*1.0)/2.0,m2=(t2*(t2-1)*1.0)/2.0,m3=(t3*(t3-1)*1.0)/2.0; dfs1(1,0),dfs2(1,0),ans=tot*2.0/((1.0*n)*(1.0*(n-1))); printf("%.2f\n%.2f\n%.2f\n",m1*ans,m2*ans,m3*ans); return 0; }