P1351 [NOIP2014 提高组] 联合权值 题解
联合权值
题意
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/51b7e61d38cc9f927014f387d15c21f3.png)
思路
- 距离为2才计算权值,我们直接枚举开头会很麻烦,我们可以枚举中间点,计算从这个点出发的其他点的权值积即可
- 在计算权值积的和的时候,两层循环暴力算会超时,我们可以找规律,发现以某个节点为中转点的联合权值之和等于权值和的平方减去权值的平方和,比如以p出发有三个点{a,b,c},那么他们之间的和就是 (a+b+c)(a+b+c) - (aa)-(bb)-(cc)
- 计算最大权值积时,只需要保存最大两个点即可
- 在mod时权值积的和需要,而算最大不用
AC代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+7;
typedef long long ll;
vector<int> v[N];
ll mod = 10007;
ll dp[N],dp2[N],W[N],vis[N];
void dfs(int u){
ll max1 = 0, max2 = 0,sum = 0,sum2 = 0;
for(int i : v[u]){
sum += W[i]%mod;
sum2 += W[i]*W[i]%mod;
if(W[i]>max1) max2 = max1,max1 = W[i];
else if(W[i]>max2) max2 = W[i];
}
dp[u] = (sum*sum%mod-sum2)%mod;
dp2[u] = max1*max2;
}
int main(){
int n;
cin>>n;
for(int i = 1;i<n;i++){
int a,b;
cin>>a>>b;
v[a].push_back(b);
v[b].push_back(a);
}
for(int i = 1;i<=n;i++) cin>>W[i];
ll ans = 0,ans_max = 0;
for(int i = 1;i<=n;i++){
dfs(i);
ans_max = max(dp2[i],ans_max);
ans = (ans+dp[i])%mod;
}
cout<< ans_max <<" "<<(ans+mod)%mod<<endl;
return 0;
}
经验总结
- 题意的转化,在一开始想的时候,我是直接枚举开头节点然后去找距离为2的点,但是这样会超时,相当于两层循环,但是要注意到题目说是距离为 2 ,这样直接枚举中间的点,它的所有相邻点都是距离为2的点
- 其次,在计算权值积的时候,一开始我也是两层暴力枚举,也会超时,没有注意到运用数学公式简化计算过程,以后遇到这种情况,要试着总结公式简化运算
- 审题还是关键,哪里要mod,哪里不用mod