luogu P3237 [HNOI2014]米特运输

传送门

谢特运输

先要搞懂题目是什么意思,简化版题意就是一棵有根树,要使得每个点都满足任意一个儿子的权值等于这个点权值除以儿子数量,问最少要修改多少个点的点权

就可以一边dfs求出每个点权值是点1的多少倍,显然一个方案至少要有一个点的权值没有被改,所以一个暴力是枚举某个点权值没有被改,然后算出其他点正确的权值,比较一下就可以得到答案

下面记点\(x\)权值为\(a_x\),正确权值是点1权值的\(b_x\)

进一步分析,可以发现一个点\(x\)不改权值,然后另一个点\(y\)也可以不改权值,当且仅当\(\frac{a_x}{b_x}=\frac{a_y}{b_y}\),就只要求出所有\(\frac{a_x}{b_x}\),问题就变成一个集合某个数最多出现了多少次,随便搞即可

然后恭喜您收获了金色传说\(\color{#FFD700}{WA}\),这精度损失大的无法想象

考虑优化精度,首先把\(b_x\)的定义改为一个点权值应该为点1权值的\(\frac{1}{b_x}\),然后推的时候只要乘法了,比较也只要\(a_xb_x=a_yb_y\)

其实还是不行,随便构造他能给你整出\(b_x=2^{100000}\),所以可以取对数,因为\(log(MN)=logM+logN\),然后上文所有乘法就可以改成加法,就能偷税的通过此题

#include<bits/stdc++.h>
#define LL long long
#define ldb long double
#define il inline
#define re register

using namespace std;
const int N=5e5+10;
il int rd()
{
    int x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
int to[N<<1],nt[N<<1],hd[N],dg[N],tot=1;
il void add(int x,int y)
{
	++tot,to[tot]=y,nt[tot]=hd[x],hd[x]=tot;
	++tot,to[tot]=x,nt[tot]=hd[y],hd[y]=tot;
	++dg[x],++dg[y];
}
ldb a[N],b[N];
int n,ans;
void dfs(int x,int ffa)
{
	a[x]=a[x]+b[x];
	for(int i=hd[x];i;i=nt[i])
	{
		int y=to[i];
		if(y==ffa) continue;
		b[y]=b[x]+log(dg[x]-(x>1)),dfs(y,x);
	}
}

int main()
{
	n=rd();
	for(int i=1;i<=n;++i) a[i]=log((ldb)rd());
	for(int i=1;i<n;++i) add(rd(),rd());
	b[1]=0,dfs(1,0);
	sort(a+1,a+n+1);
	for(int i=1,j=1;i<=n;++j,i=j)
	{
		while(j<n&&fabs(a[j+1]-a[j])<1e-5) ++j;
		ans=max(ans,j-i+1);
	}
	printf("%d\n",n-ans);
	return 0;
}
posted @ 2019-02-21 17:29  ✡smy✡  阅读(95)  评论(0编辑  收藏  举报