题目好难懂。。
简述题意:给出一棵树,要求满足两个要求:
1.每个节点的子节点权值相等。
2.每个节点的权值和是这个节点的权值。
然后就出最小修改点的数量来满足这个要求。
那么显而易见只要确定一个节点就可以确定群图节点权值。这样枚举是的。
通过这个结论我们发现只要确定1就可以确定全图,所以可以通过让每一个节点权值不动,然后判断有多少个1最后的值相同,
就是最多有多少个不用动的,因为1选这个值可以同理出这些点不动。用总和减去这个值就是答案。
所以我们可以优化一下,dfs遍历这棵树,每到一个节点的时候就乘上这个点子节点的个数,代表如果这个点被作为不动的值
因为这个节点是子节点的和,所以到这个点不动,就是这层都是这个值,就是它父节点的子节点个数的值,向上推同理。
所以可以从根推下来,到每一个位置只要乘上自己的权值就是这个点不动的时候1的权值。
然后因为值太大了,可以通过两种方式优化,一种是取log,把乘法变成加法。
另一种是找一个模数,把大数拆成这个模数的倍数加余数。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
int n;
struct node
{
int to,nxt;
}edge[1000005];
double cmp(double x,double y)
{
return x<y;
}
int head[500005],cnt=1,son[500005],val[500005],ans=1,tot;
double f[500005];
void init()
{
memset(head,-1,sizeof(head));
}
void add(int from,int to)
{
edge[cnt].to=to;
edge[cnt].nxt=head[from];
head[from]=cnt++;
}
void dfs(int u,int fa,double v)
{
f[u]=v+(double)log((double)val[u]);
for(int i=head[u];i!=-1;i=edge[i].nxt)
{
int to=edge[i].to;
if(to==fa)continue;
dfs(to,u,v+(double)log(double(son[u])));
}
}
int main()
{
scanf("%d",&n);
init();
for(int i=1;i<=n;i++)scanf("%d",&val[i]);
for(int i=1;i<=n-1;i++)
{
int a,b;
scanf("%d%d",&a,&b);
add(a,b);add(b,a);
son[a]++;son[b]++;
}
for(int i=2;i<=n;i++)son[i]--;
dfs(1,1,(double)log(1.0));
sort(f+1,f+1+n,cmp);
for(int i=1;i<n;i++)
{
if(f[i+1]-f[i]<=1e-9)ans++,tot=max(tot,ans);
else ans=1;
}
printf("%d",n-tot);
return 0;
}