题干真长
思路分析:
当我们把样例画出来后,我们发现,我们只要确定一个点的权值,那么这棵树所有点的权值就已知了,例如,我们设2号节点权值已知且为2,那么我们依据题意想要让他符合要求必须有5,4号节点权值为1;3号节点权值为2;1号节点权值为4。
所以我们可以枚举树上的所有的点,求出该节点不动时根节点(即1号)的权值,如果两个不同节点不动时根节点值相同,那么他们一定是隶属于相同的一棵树的(这相当于根节点确定,子节点都相应确定),我们求出每一种情况后对节点数减去相同数取最小值即可(因为我们确定一个节点后所建的树一定是符合要求的,相同数即为不用动的点)。
在计算根节点过程中由于我们要不断地乘(因为根节点权值一定是所有树上除根节点外结点的权值总和,可以根据上图模拟一下),如果硬乘会爆long long,所以我们可以对乘法的每一项取log,可将乘法转化为加法,但注意在判等时应该是他们两个的差小于1e-6,注意精度。
上代码:
1 #include<cstdio> 2 #include<cmath> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int N=1e6+10; 7 const double jingdu=1e-6; //精度用于判等 8 int n,Head[N],tot,out[N]; 9 int capacity[N]; //表示容量,capacity是容量的英文 10 struct Node{ 11 int next,to; 12 }e[N]; 13 void Add(int x,int y){ 14 e[++tot].to=y; 15 e[tot].next=Head[x]; 16 Head[x]=tot; 17 } 18 double val[N]; //可能是小数用double 19 void dfs(int x,int fa,double ans){ 20 val[x]=ans+log(capacity[x]); //递归,求出每个点不动时根节点的权值. 21 out[x]--; //注意这里的加法实际上是乘法,因为用到了log,out--是减去父亲的那条边 22 for(int i=Head[x];i;i=e[i].next){ 23 int v=e[i].to; 24 if(v==fa) continue; 25 dfs(v,x,ans+log(out[x])); 26 } 27 } 28 int Max=0,sum=1; 29 int main(){ 30 scanf("%d",&n); 31 for(int i=1;i<=n;++i) 32 scanf("%d",&capacity[i]); 33 for(int i=1;i<n;++i){ 34 int x,y; 35 scanf("%d%d",&x,&y); 36 Add(y,x);Add(x,y);out[x]++;out[y]++; //记录出度 37 } 38 out[1]++,dfs(1,0,0);//根节点本不应该--,这里先给它加上 39 sort(val+1,val+1+n);//小技巧判等数量 40 for(int i=2;i<=n;++i){ 41 if(val[i]-val[i-1]<jingdu) sum++; 42 else Max=max(Max,sum),sum=1; 43 } 44 printf("%d ",n-Max); 45 return 0; 46 }