首页 > 其他 > 详细

BZOJ 4675(点分治)

时间:2019-01-29 15:31:10      阅读:175      评论:0      收藏:0      [点我收藏+]

题面

传送门

分析

由于期望的线性性,我们可以分别计算每个点对对答案的贡献

有三个人取数字,分开对每个人考虑

设每个人分别取了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(点分治)

原文:https://www.cnblogs.com/birchtree/p/10333939.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!