【题解&&海亮集训&&dp】 洛谷 P1351 联合权值
作者:互联网
题目传送门
题目描述:
无向连通图 GGG 有 nnn 个点,n−1n-1n−1 条边。点从 111 到 nnn 依次编号,编号为 iii 的点的权值为 WiW_iWi,每条边的长度均为 111。图上两点 (u,v)(u, v)(u,v) 的距离定义为 uuu 点到 vvv 点的最短距离。对于图 GGG 上的点对 (u,v)(u, v)(u,v),若它们的距离为 222,则它们之间会产生Wv×WuW_v \times W_uWv×Wu 的联合权值。
请问图 GGG 上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少?
输入输出格式
输入格式:
第一行包含 111 个整数 nnn。
接下来 n−1n-1n−1 行,每行包含 222 个用空格隔开的正整数 u,vu,vu,v,表示编号为 uuu 和编号为 vvv 的点之间有边相连。
最后 111 行,包含 nnn 个正整数,每两个正整数之间用一个空格隔开,其中第 iii 个整数表示图 GGG 上编号为 iii 的点的权值为 WiW_iWi。
输出格式:
输出共 111 行,包含 222 个整数,之间用一个空格隔开,依次为图 GGG 上联合权值的最大值和所有联合权值之和。由于所有联合权值之和可能很大,输出它时要对100071000710007取余。
分析:因为题目说两个点之间的距离为2就是一个有序数对,所以我们就可以知道一对有序序对之间必定隔着一个中转点,例如下图:
其中1、2、3、4互为序对,因为它们都隔着一个中转点5,它们之间的距离都为2,所以做这道题的基础就是枚举中转点,它延伸出去的点必定互为序对。
那么枚举的问题解决了,这回我们就需要想算的问题。
第一个求最大值很好理解,我们只需要枚举中间点,在枚举它延伸出去的点,然后找一个最大值和次大值就可以了。
例如上图中的最大值就是 c*d=12;
第二个问题需要用到乘法的一些定则。
举个例子:
上图的和为a∗b+a∗c+a∗d+b∗a+b∗c+b∗d+c∗a+c∗b+c∗d+d∗a+d∗c+d∗b
经过整理可以变成:
(a∗b+a∗c+a∗d+b∗c+b∗d+c∗d)∗2
再经过整理可以变成:
(a∗(b+c+d)+b∗(c+d)+c∗d)∗2
所以我们发现求和只需要用一个sum记录前几个数求得的和,然后不停的用后面的数累乘即可,具体的证明可以用乘法分配律展开求得。
不过求得后最后的乘2不能忘掉,这也是一个坑点。
那么具体代码如下:
#include<bits/stdc++.h>
using namespace std;
#define P 10007
struct node{
int next,y;
}e[1000001];
int v[1000001];
int n;
int linkk[10000001];
int len;
void insert(int xx,int yy){
e[++len].next=linkk[xx];
linkk[xx]=len;
e[len].y=yy;
}
int main(){
scanf("%d",&n);
for (int i=1,x,y;i<n;i++) scanf("%d %d",&x,&y),insert(x,y),insert(y,x);
int ans=0,maxx=0;
for (int i=1;i<=n;i++) scanf("%d",&v[i]);
for (int i=1;i<=n;i++){
int max1=0,max2=0,sum=0;
int t=linkk[i];
for (int j=t;j;j=e[j].next){
if (v[e[j].y]>max1) max2=max1,max1=v[e[j].y];
else max2=max(max2,v[e[j].y]);
ans=(ans+sum*v[e[j].y])%P;
sum=(sum+v[e[j].y])%P;
}
maxx=max(maxx,max1*max2);
}
printf("%d %d",maxx,(ans*2)%P);
return 0;
}
标签:洛谷,int,题解,sum,GGG,111,&&,权值,1n 来源: https://blog.csdn.net/huang_ke_hai/article/details/86682061