P1351 联合权值
联合权值
然而这只是一道普及+/提高的大水题
这道题是2014年提高组day1的第二题。
简单题意就是在树上每个点都有权值,相邻两点的距离为1,求距离为2的点的权值乘积的和以及最大值。
基本思路就是遍历整棵树,然后找到距离该点距离为2的点,计算距离,更新最大值和乘积和。
但这样就很慢了。所以我们可以遍历中间点,然后找到中间所连接的点,计算他们两两之间的乘积。
以上图为例:
先找到节点1,遍历与1相连的节点,有2,3,4。
计算与2距离为2的节点,有3,4。所以乘积和为dis[2]*(dis[3]+dis[4])。
计算与3距离为2的节点,有2,4。所以乘积和为dis[3]*(dis[2]+dis[4])。
计算与4距离为2的节点,有2,3。所以乘积和为dis[4]*(dis[2]+dis[3])。
这样计算还是比较麻烦,怎么简化呢?
我们可以通过转化式子来达到此目的。
dis[2]*(dis[3]+dis[4])就可以转化成dis[2]*dis(dis[2]+dis[3]+dis[4])-dis[2]^2。
dis[3]*(dis[2]+dis[4])就可以转化成dis[3]*dis(dis[2]+dis[3]+dis[4])-dis[3]^2。
dis[4]*(dis[2]+dis[3])就可以转化成dis[4]*dis(dis[2]+dis[3]+dis[4])-dis[4]^2。
把他们放到一起,就是(dis[2]+dis[3]+dis[4])^2-dis[2]^2-dis[3]^2-dis[4]^2。
也就是点1连接的所有点的权值和的平方减去平方和,就可以计算出答案了。
1 #include<cstdio> 2 #include<iostream> 3 #define N 1000000 4 #define M 200010 5 using namespace std; 6 long long next[N],to[N],head[M],num,j,dis[M],ans1,ans2,ans,n,a,b,mmax=-1; 7 void add(long long false_from,long long false_to){ 8 next[++num]=head[false_from]; 9 to[num]=false_to; 10 head[false_from]=num; 11 } 12 void dfs(long long x){ 13 ans1=0; 14 ans2=0; 15 long long k=1,mmax1=-1,mmax2=-1; 16 for(long long i=head[x];i;i=next[i]){ 17 j=next[i]; 18 if(i==head[x]&&!j){ 19 k=0; 20 break; 21 } 22 if(dis[to[i]]>mmax1) 23 mmax1=dis[to[i]]; 24 else 25 if(dis[to[i]]>mmax2) 26 mmax2=dis[to[i]]; 27 ans1+=dis[to[i]]; 28 ans2+=dis[to[i]]*dis[to[i]]; 29 } 30 if(k){ 31 mmax=max(mmax,mmax1*mmax2); 32 ans+=ans1*ans1-ans2; 33 ans%=10007; 34 } 35 } 36 int main(){ 37 scanf("%lld",&n); 38 for(long long i=1;i<n;++i){ 39 scanf("%lld%lld",&a,&b); 40 add(a,b); 41 add(b,a); 42 } 43 for(long long i=1;i<=n;++i) 44 scanf("%lld",&dis[i]); 45 for(long long i=1;i<=n;++i) 46 dfs(i); 47 printf("%lld %lld",mmax,ans); 48 return 0; 49 }