#16. 【NOIP2014】联合权值
描述
输入格式
第一行包含 11 个整数 nn。
接下来 n−1n−1 行,每行包含 22 个用空格隔开的正整数 u,vu,v,表示编号为 uu 和编号为 vv 的点之间有边相连。
最后 11 行,包含 nn 个正整数,每两个正整数之间用一个空格隔开,其中第 ii 个整数表示图 GG 上编号为 ii 的点的权值为 WiWi。
输出格式
输出共 11 行,包含 22 个整数,之间用一个空格隔开,依次为图 GG 上联合权值的最大值和所有联合权值之和。由于所有联合权值之和可能很大,输出它时要对1000710007取余。
样例一
input
5 1 2 2 3 3 4 4 5 1 5 2 3 10
output
20 74
explanation
距离为 2 的有序点对有(1,3),(2,4),(3,1),(3,5),(4,2),(5,3)(1,3),(2,4),(3,1),(3,5),(4,2),(5,3)。其联合权值分别为 2,15,2,20,15,202,15,2,20,15,20。其中最大的是 2020,总和为 7474。
限制与约定
对于30%的数据,1<n≤1001<n≤100;
对于60%的数据,1<n≤20001<n≤2000;
对于100%的数据,1<n≤200000,0<Wi≤100001<n≤200000,0<Wi≤10000。
保证一定存在可产生联合权值的有序点对。
时间限制:1s1s
内存限制:128MB
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mod = 10007;
int first[233333], next[500000], tot = 0, num[233333], sum[233333], maxx = -1;
struct qer
{
int from, to;
}q[500000];
struct maxmin
{
int mx, mn;
}mm[233333];
void build(int f, int t)
{
q[++tot] = (qer){f, t};
next[tot] = first[f];
first[f] = tot;
}
int main()
{
memset(first, -1, sizeof(first));
int n;
cin>>n;
int u, v;
for(int i = 1;i < n;i ++)
{
scanf("%d%d", &u, &v);
build(u, v);
build(v, u);
}
for(int i = 1;i <= n;i ++)
scanf("%d", &num[i]);
for(int i = 1;i <= n;i ++)
{
for(int j = first[i];j != -1;j = next[j])
{
int u = q[j].to;
sum[i] += num[u];
sum[i] %= mod;
if(num[u] >= mm[i].mx)
{
mm[i].mn = mm[i].mx;
mm[i].mx = num[u];
}
else if(num[u] > mm[i].mn)
mm[i].mn = num[u];
}
}
long long ans = 0, lsans;
for(int i = 1;i <= n;i ++)
{
for(int j = first[i];j != -1;j = next[j])
{
int v = q[j].to;
lsans = (sum[i] - num[v])*num[v];
lsans+=mod*10;
// cout<<lsans<<" lsans v "<<v<<" sum[i] "<<sum[i]<<" num[v] "<<num[v]<<endl;
ans += lsans;
ans %= mod;
}
maxx = max(maxx, mm[i].mx * mm[i].mn);
}
while(ans<0)
ans+=mod;
cout<<maxx<<" "<<ans;
return 0;
}