BZOJ 4675(點分治)
阿新 • • 發佈:2019-01-29
include ref scan num != 點分治 分析 強制 n-k
題面
傳送門
分析
由於期望的線性性,我們可以分別計算每個點對對答案的貢獻
有三個人取數字,分開對每個人考慮
設每個人分別取了k個數,則一共有\(C_n^k\)種組合,選到每種組合的概率為\(\frac{1}{C_n^k}\)
對於一個幸運點對,包含它的組合有\(C_{n-2}^{k-2}\)種(k個點中有2個點是該點對,再從剩下的n-2個點中選k-2個點,每種的貢獻均為1)
所以每一個點對的貢獻是
\[\frac{C_{n-2}^{k-2}}{C_n^k}=\frac{\frac{(n-2)!}{(n-k)!\times (k-2)!}}{\frac{n!}{(n-k)! \times k !}}=\frac{(n-2)! \times k !}{n! \times (k-2)!}=\frac{k(k-1)}{n(n-1)}\]
因此總答案為\(a \times \frac{k(k-1)}{n(n-1)}\),其中a為幸運點對的數量
所以只要求出幸運點對數量即可
對於每一個幸運數num[i],我們進行一次點分治,求出長度為num[i]的路徑數(直接套點分治板子,先求長度>=num[i]的路徑數,再減去長度>num[i]的路徑數量),並累計進答案
註意最後n*(n-1)要用double,否則會爆int
代碼
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 50005 using namespace std; int n,k; struct edge{ int from; int to; int next; }E[maxn<<1]; int ecnt=1; int head[maxn]; inline void add_edge(int u,int v){ ecnt++; E[ecnt].from=u; E[ecnt].to=v; E[ecnt].next=head[u]; head[u]=ecnt; } int root=0,sum; int f[maxn]; int sz[maxn]; int vis[maxn]; void get_root(int x,int fa){ sz[x]=1; f[x]=0; for(int i=head[x];i;i=E[i].next){ int y=E[i].to; if(y!=fa&&!vis[y]){ get_root(y,x); sz[x]+=sz[y]; f[x]=max(f[x],sz[y]); } } f[x]=max(f[x],sum-f[x]); if(f[x]<f[root]) root=x; } int cnt=0; int deep[maxn]; int res[maxn]; void get_deep(int x,int fa){ res[++cnt]=deep[x]; for(int i=head[x];i;i=E[i].next){ int y=E[i].to; if(y!=fa&&!vis[y]){ deep[y]=deep[x]+1; get_deep(y,x); } } } int calc(int x,int d0){ deep[x]=d0; cnt=0; get_deep(x,0); sort(res+1,res+1+cnt); int l=1,r=cnt; int ans1=0; while(l<r){ if(res[l]+res[r]<=k){ ans1+=(r-l); l++; }else r--; } l=1,r=cnt; int ans2=0; while(l<r){ if(res[l]+res[r]<k){ ans2+=(r-l); l++; }else r--; } return ans1-ans2; } int ans=0; void solve(int x){ vis[x]=1; ans+=calc(x,0); for(int i=head[x];i;i=E[i].next){ int y=E[i].to; if(!vis[y]){ ans-=calc(y,1); root=0; sum=sz[y]; get_root(y,0); solve(root); } } } void divide_ini(){ memset(deep,0,sizeof(deep)); memset(f,0,sizeof(f)); memset(sz,0,sizeof(sz)); memset(vis,0,sizeof(vis)); root=0; sum=n; f[0]=n; get_root(1,0); } int m; int num[maxn]; int main(){ int u,v; scanf("%d %d",&n,&m); for(int i=1;i<=m;i++){ scanf("%d",&num[i]); } for(int i=1;i<n;i++){ scanf("%d %d",&u,&v); add_edge(u,v); add_edge(v,u); } for(int i=1;i<=m;i++){ k=num[i]; divide_ini(); solve(root); } double k1,k2,k3; if(n%3==0) k1=k2=k3=n/3; else if(n%3==1){ k1=n/3+1; k2=n/3; k3=n/3; }else{ k1=n/3+1; k2=n/3+1; k3=n/3; } printf("%.2lf\n",ans*k1*(k1-1)/((double)n*(n-1)));//強制轉成double,防止溢出 printf("%.2lf\n",ans*k2*(k2-1)/((double)n*(n-1))); printf("%.2lf\n",ans*k3*(k3-1)/((double)n*(n-1))); }
BZOJ 4675(點分治)