题目描述
无向连通图 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,则它们之间会产生WuW_uWu×××WvW_vWv的联合权值。
请问图 G 上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少?
输入格式
第一行包含111个整数 nnn。
接下来 n−1n-1n−1 行,每行包含 222 个用空格隔开的正整数 u、vu、vu、v,表示编号为 uuu 和编号为 vvv 的点 之间有边相连。
最后 111 行,包含 nnn 个正整数,每两个正整数之间用一个空格隔开,其中第 iii 个整数表示 图 GGG 上编号为 iii 的点的权值为WiW_iWi。
输出格式
输出共 111 行,包含 222 个整数,之间用一个空格隔开,依次为图 GGG 上联合权值的最大值 和所有联合权值之和。由于所有联合权值之和可能很大,输出它时要对 100071000710007 取余。
样例输入
555
111 222
222 333
333 444
444 555
111 555 222 333 101010
样例输出
202020 747474
限制
对于 303030%的数据,1<n≤1001 < n ≤ 1001<n≤100;
对于 606060%的数据,1<n≤20001 < n ≤ 20001<n≤2000;
对于 100100100%的数据,1<n≤200,000,0<Wi≤10,0001 < n ≤ 200,000,0 < W_i ≤ 10,0001<n≤200,000,0<Wi≤10,000。
解析
原图就是一棵无根树啊。
树上u,vu,vu,v两点间距离为222只有两种情况:
- u,vu,vu,v之间是祖父与孙子的关系
- u,vu,vu,v之间是兄弟的关系
因为一个节点至多有一个祖父节点,而有可能有多个孙子节点,所以我们可以找每个节点的祖父节点,并将和的答案乘222即可。
对于第二种情况我们只要统计每个节点权值最大和次大的儿子节点即可求出最大值。
令S=∑v∈son[u]w[v]{S=\sum_{v∈son[u]} w[v]}S=∑v∈son[u]w[v]
T=∑v∈son[u]w[v]2{T=\sum_{v∈son[u]} w[v]^2}T=∑v∈son[u]w[v]2
至于和,对于节点uuu,它的儿子的权值和即为
S2−TS^2-TS2−T
轻松解决此题。
代码
#include<cstdio>
using namespace std;
const int p = 10007;
const int maxn = 200005;
const int maxe = 200005;
int edgenum;
int Next[maxe << 1] , vet[maxe << 1] , head[maxn];
int fa[maxn] , mx[maxn] , cmx[maxn] , sum[maxn] , sqr_sum[maxn];
int w[maxn];
int max(int x , int y){return x > y ? x : y;}
int sqr(int x){return x * x;}
int read()
{
char ch = getchar();
while(ch < '0' || ch > '9') ch = getchar();
int res = 0;
while(ch >= '0' && ch <= '9') res = (res << 3) + (res << 1) + (ch ^ 48) , ch = getchar();
return res;
}
void clear_edge(int n)
{
edgenum = 0;
for(int i = 1;i <= n;i++) head[i] = 0;
}
void add_edge(int u , int v)
{
edgenum++;
Next[edgenum] = head[u];
vet[edgenum] = v;
head[u] = edgenum;
}
void dfs(int u , int father)
{
fa[u] = father;
mx[u] = 0;
cmx[u] = 0;
sum[u] = 0;
sqr_sum[u] = 0;
for(int e = head[u];e;e = Next[e])
{
int v = vet[e];
if(v == fa[u]) continue;
dfs(v , u);
if(w[v] > mx[u]) cmx[u] = mx[u] , mx[u] = w[v];
else if(w[v] > cmx[u]) cmx[u] = w[v];
sum[u] = (sum[u] + w[v]) % p;
sqr_sum[u] = (sqr_sum[u] + sqr(w[v]) % p) % p;
}
}
int main()
{
freopen("link.in","r",stdin);
freopen("link.out","w",stdout);
int n = read();
clear_edge(n);
for(int i = 1;i < n;i++)
{
int u = read() , v = read();
add_edge(u , v) , add_edge(v , u);
}
for(int i = 1;i <= n;i++) w[i] = read();
dfs(1 , 0);
int ans_max = 0 , ans_tot = 0;
for(int i = 1;i <= n;i++)
if(fa[fa[i]] != 0)
{
ans_max = max(ans_max , w[i] * w[fa[fa[i]]]);
ans_tot = (ans_tot + w[i] * w[fa[fa[i]]] % p) % p;
}
ans_tot = (ans_tot << 1) % p;
for(int i = 1;i <= n;i++)
{
ans_max = max(ans_max , mx[i] * cmx[i]);
ans_tot = (ans_tot + sqr(sum[i]) % p - sqr_sum[i] + p) % p;
}
printf("%d %d\n",ans_max,ans_tot);
return 0;
}