联合权值(树上小操作)
程序员文章站
2022-03-03 11:34:00
...
题目描述
无向连通图 有 个点, 条边。点从 到 依次编号,编号为 的点的权值为 , 每条边的长度均为 。图上两点的距离定义为 点到 点的最短距离。对于图 上的点对,若它们的距离为 ,则它们之间会产生的联合权值。
请问图 G 上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少?
输入格式
第一行包含个整数 。
接下来 行,每行包含 个用空格隔开的正整数 ,表示编号为 和编号为 的点 之间有边相连。
最后 行,包含 个正整数,每两个正整数之间用一个空格隔开,其中第 个整数表示 图 上编号为 的点的权值为。
输出格式
输出共 行,包含 个整数,之间用一个空格隔开,依次为图 上联合权值的最大值 和所有联合权值之和。由于所有联合权值之和可能很大,输出它时要对 取余。
样例输入
样例输出
限制
对于 %的数据,;
对于 %的数据,;
对于 %的数据,。
解析
原图就是一棵无根树啊。
树上两点间距离为只有两种情况:
- 之间是祖父与孙子的关系
- 之间是兄弟的关系
因为一个节点至多有一个祖父节点,而有可能有多个孙子节点,所以我们可以找每个节点的祖父节点,并将和的答案乘即可。
对于第二种情况我们只要统计每个节点权值最大和次大的儿子节点即可求出最大值。
令
至于和,对于节点,它的儿子的权值和即为
轻松解决此题。
代码
#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;
}