联合权值
题目描述
无向连通图 G 有 n 个点,n-1 条边。点从 1 到 n依次编号,编号为 i 的点的权值为 W_i,每条边的长度均为 1。图上两点 (u, v) 的距离定义为 u 点到 v 点的最短距离。对于图 G 上的点对 (u, v),若它们的距离为 2,则它们之间会产生Wv×Wu 的联合权值。
请问图 G 上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少?
输入格式
第一行包含 1 个整数 n。
接下来 n−1 行,每行包含 2 个用空格隔开的正整数 u,v,表示编号为 u 和编号为 v 的点之间有边相连。
最后 1 行,包含 n 个正整数,每两个正整数之间用一个空格隔开,其中第 i 个整数表示图 G 上编号为 i 的点的权值为 W_i。
输出格式
输出共 1 行,包含 2个整数,之间用一个空格隔开,依次为图 G 上联合权值的最大值和所有联合权值之和。由于所有联合权值之和可能很大,输出它时要对10007取余。
输入输出样例
输入 #1
5 1 2 2 3 3 4 4 5 1 5 2 3 10
输出 #1
20 74
分析:
本题有个非常显然的结论:就是能产生联合权值的都是有一个中间点遍历得到的两个点,于是就算法十分显然,遍历每一个点然后寻找相邻结点就完事了。
但是:众所周知,NOIP是毒瘤,所以没这么简单,如果只是暴力统计答案会超时。
所以我们用一种奇妙的方法来统计就行了。(完工)
不过话说这题算法是:暴力?(huaji)
CODE:
1 #include<cmath> 2 #include<cstdio> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 const int M=400005; 8 const int mod=10007; 9 int n; 10 int tot,head[M],to[M],next[M]; 11 int w[M],maxn,ans; 12 int get(){ 13 int res=0,f=1; 14 char c=getchar(); 15 while (c>'9'||c<'0') { 16 if (c=='-') f=-1; 17 c=getchar(); 18 } 19 while (c<='9'&&c>='0'){ 20 res=(res<<3)+(res<<1)+c-'0'; 21 c=getchar(); 22 } 23 return res*f; 24 } 25 void add(int u,int v){ 26 next[++tot]=head[u]; 27 head[u]=tot; 28 to[tot]=v; 29 return ; 30 } 31 int sum,maxx,ansl; 32 int main(){ 33 n=get(); 34 for (int i=1;i<n;i++){ 35 int u=get(),v=get(); 36 add(u,v);add(v,u); 37 } 38 for (int i=1;i<=n;i++) w[i]=get(); 39 for (int i=1;i<=n;i++){ 40 sum=w[to[head[i]]]; 41 maxx=w[to[head[i]]]; 42 ansl=0; 43 //cout<<i<<endl; 44 for (int j=next[head[i]];j;j=next[j]){ 45 maxn=max(maxn,maxx*w[to[j]]); 46 maxx=max(maxx,w[to[j]]); 47 ansl+=sum%mod*w[to[j]]%mod; 48 sum+=w[to[j]]%mod; 49 //cout<<maxx<<" "<<maxn<<" "<<ansl<<" "<<sum<<endl; 50 } 51 ans+=ansl*2; 52 ans%=mod; 53 //cout<<ans<<endl<<endl; 54 } 55 printf ("%d %d\n",maxn,ans); 56 return 0; 57 }