[BZOJ 3573] 米特运输

Link:

BZOJ 3573 传送门

Solution:

一道语文题

 

转化后的题意就是使得每个节点的权值都等于 父亲节点的权值/儿子数 的最小操作数

能发现一条重要的性质:只要一个节点确定,所有节点的权值都确定了

 

于是我们只要枚举$1……n$每个节点权值不变,

算出根节点$root$的权值出现过的最多次数$res$,用$n-res$即是结果

 

但如果$O(n^2)$枚举不仅TLE,而且会爆精度

于是我们将权值都用$log$来表示:

(1)算出当根节点$root$为“单位一”:$log(1)$时,各个节点的权值$t_i$

(2)对于节点$v$,$t_v*log(原权值)$就能算出此时根节点在$log$下的权值,接着统计即可

 

Code:

#include <bits/stdc++.h>

using namespace std;

const int MAXN=5e5+10;
const double eps=1e-6;
double t[MAXN],top[MAXN];
int n,dat[MAXN],siz[MAXN];
vector<int> G[MAXN];

void dfs(int x,int anc)
{
    for(int i=0;i<G[x].size();i++)
    {
        int v=G[x][i];
        if(v==anc) continue;
        t[v]=t[x]+log(siz[x]);dfs(v,x);
    }
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&dat[i]);
    for(int i=1;i<n;i++)
    {
        int x,y;scanf("%d%d",&x,&y);
        G[x].push_back(y);G[y].push_back(x);
        siz[x]++;siz[y]++;
    }
    for(int i=2;i<=n;i++) siz[i]--;    
    
    t[1]=log(1);dfs(1,0);
    for(int i=1;i<=n;i++) top[i]=t[i]+log(dat[i]);
    sort(top+1,top+n+1);
    
    int res=0,cur=1;
    for(int i=2;i<=n;i++)
        if(top[i]-top[i-1]<eps) cur++;
        else res=max(res,cur),cur=1;
    res=max(res,cur);
    printf("%d",n-res); 
    return 0;
}

 

Review:

1、出现只涉及比较,但不用输出确切值的高精度问题时,

使用$log$或$hash$即可,不用高精度类

 

2、“单位一”的使用

这里的“单位一”用得很妙啊,

如果每次只要乘上相应倍数即可$O(1)$得解,可以用“单位一”的思想先预处理

 

posted @ 2018-06-01 12:08  NewErA  阅读(138)  评论(0编辑  收藏  举报