算法:树型DP
难度:NOIP+
简述题意:
给定一棵以1为根的树,每个节点又有一个权值。问:最少要改变多少个节点的权值,使得:
(1)每个节点的子节点的权值相同
(2)每个节点的子节点权值之和等于该点的权值
【数据范围】
对于20%的数据满足N≤20;
对于50%的数据满足N≤2000;
对于100%的数据满足N≤500000, A[i]≤10^8。
题解:
对于20分的数据:直接暴力枚举每个点是否需要被修改,然后检验,取最优方案即可,复杂度。
对于50分的数据:因为我们可以保证至少有一个点不被修改,然后我们就可以枚举这个点,然后暴力判断其他点是否能在这个点不变的情况下不变,取最优方案即可,复杂度
对于100分的数据:当根节点(假定为1号节点)的值确定时,然后每个节点的值都会被确定,因为树的结构是确定的。好的,然后我们求出每个点的值不变的时候,根节点的值是多少,然后排序,取众数即可,最后的答案ans=n-众数出现次数。
由于数据特别大,所以使用log将乘法压缩为加法来保存(常用)/hash。
注意一下浮点数误差的处理
时间复杂度:
代码如下:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <string>
#include <cmath>
#include <algorithm>
#define ll long long
#define D double
#define N 500005
using namespace std;
struct node
{
int next;
int to;
}edge[N<<1];
int head[N],a[N];
int cnt=1;
void init()
{
memset(head,-1,sizeof(head));
cnt=1;
}
void add(int u,int v)
{
edge[cnt].next=head[u];
edge[cnt].to=v;
head[u]=cnt++;
}
int son[N];
D f[N];
void dfs(int rt,D sum,int fa)
{
f[rt]=sum+log((D)a[rt]);
for(int i = head[rt];i != -1;i=edge[i].next)
{
int to=edge[i].to;
if(to==fa) continue;
dfs(to,sum+log((D)son[rt]),rt);//刚才居然忘记取log
}
}
int main()
{
init();
int n;
scanf("%d",&n);
for(int i = 1;i <= n;i++)
{
scanf("%d",&a[i]);
}
for(int i = 1;i < n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
son[x]++;
son[y]++;
}
for(int i = 2;i <= n;i++)
{
son[i]--;
}
dfs(1,(D)log(1.0),-1);
sort(f+1,f+1+n);
int ans=0,ctt=0;
for(int i = 1;i < n;i++)
{
if(abs(f[i+1]-f[i])<=1e-9)
{
ctt++;
ans=max(ans,ctt);
}else
{
ctt=1;
}
}
ans=max(ans,ctt);//!!!
printf("%d\n",n-ans);
return 0 ;
}