bzoj 4675 点对游戏 Posted on 2019-05-23 | Views: 点分治. 表面上看是个期望的题,然而跟期望没多大关系. 每个人的答案显然是 能选的点对数目/总点对数目*幸运点对的总数目 . 只需要求幸运点对的总数目.由于 $m$ 很小,所以直接点分治就可以了.时间复杂度 $O(nmlogn)$ . 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126#include<bits/stdc++.h>using namespace std;typedef long long ll;inline int read(){ int out=0,fh=1; char jp=getchar(); while ((jp>'9'||jp<'0')&&jp!='-') jp=getchar(); if (jp=='-') fh=-1,jp=getchar(); while (jp>='0'&&jp<='9') out=out*10+jp-'0',jp=getchar(); return out*fh;}const int inf=1e9;const int MAXN=5e4+10;int n,m,luckynum[11];ll sum=0;int ecnt=0,head[MAXN],nx[MAXN<<1],to[MAXN<<1];void addedge(int u,int v){ ++ecnt; to[ecnt]=v; nx[ecnt]=head[u]; head[u]=ecnt;}int totsiz,siz[MAXN],mi,rt,vis[MAXN];void getroot(int u,int fa){ siz[u]=1; int sonsiz=0; for(int i=head[u];i;i=nx[i]) { int v=to[i]; if(vis[v] || v==fa) continue; getroot(v,u); siz[u]+=siz[v]; sonsiz=max(sonsiz,siz[v]); } sonsiz=max(sonsiz,totsiz-siz[u]); if(sonsiz<mi) mi=sonsiz,rt=u;}int dis[MAXN],bucket[MAXN],stk1[MAXN],tp1=0,stk2[MAXN],tp2=0;void getdis(int u,int fa){ stk2[++tp2]=u; dis[u]=dis[fa]+1; for(int i=1;i<=m;++i) if(luckynum[i]>=dis[u]) sum+=bucket[luckynum[i]-dis[u]]; for(int i=head[u];i;i=nx[i]) { int v=to[i]; if(vis[v] || v==fa) continue; getdis(v,u); }}void solve(int u){ while(tp1) bucket[dis[stk1[tp1--]]]=0; dis[u]=0;//先清空,再设dis[u]=0,否则可能清空错误 bucket[0]=1; for(int i=head[u];i;i=nx[i]) { int v=to[i]; if(vis[v]) continue; tp2=0; getdis(v,u); while(tp2) { stk1[++tp1]=stk2[tp2]; ++bucket[dis[stk2[tp2]]]; --tp2; } }}void divide(int u){ solve(u); vis[u]=1; for(int i=head[u];i;i=nx[i]) { int v=to[i]; if(vis[v]) continue; mi=inf,totsiz=siz[v]; getroot(v,0); divide(rt); }}void getsum(){ mi=inf,totsiz=n; getroot(1,0); divide(rt);}double calc(ll pairs){ return (double(pairs)) * (double(sum)) / (double(1LL * n * (n-1) )) ;}int main(){ n=read(),m=read(); for(int i=1;i<=m;++i) luckynum[i]=read(); for(int i=1;i<n;++i) { int u=read(),v=read(); addedge(u,v); addedge(v,u); } getsum(); int p=(n+2)/3; printf("%.2f\n",calc(1LL*p*(p-1))); p=(n+1)/3; printf("%.2f\n",calc(1LL*p*(p-1))); p=n/3; printf("%.2f\n",calc(1LL*p*(p-1))); return 0;}