1. 程式人生 > >[Noip2014] 聯合權值

[Noip2014] 聯合權值

() 一行 oid scan dfs using ont ++ input

Description

無向連通圖 G 有 n 個點,n - 1 條邊。點從1 到n 依次編號,編號為 i 的點的權值為 W i ,每條邊的長度均為 1 。圖上兩點 ( u , v ) 的距離定義為 u 點到 v 點的最短距離。對於圖 G 上的點對 (u, v) ,若它們的距離為 2 ,則它們之間會產生 Wu×Wv 的聯合權值。

請問圖 G 上所有可產生聯合權值的有序點對中,聯合權值最大的是多少?所有聯合權值之和是多少?

Input

第一行包含1 個整數 n 。

接下來 n - 1 行,每行包含 2 個用空格隔開的正整數 u 、v ,表示編號為 u 和編號為 v 的點之間有邊相連。

最後1 行,包含 n 個正整數,每兩個正整數之間用一個空格隔開,其中第 i 個整數表示圖 G 上編號為i 的點的權值為 W i 。

Output

輸出共1行,包含2個整數,之間用一個空格隔開,依次為圖 G 上聯合權值的最大值和所有聯合權值之和。由於所有聯合權值之和可能很大,輸出它時要對10007 取余。

Solution

明顯的樹形結構,肯定會在 dfs 裏瞎搞。

隨便選一個點為根,那麽距離為 2 的點對一定是

1. 兒子結點和父節點

2. 互為兄弟節點

所以我們在 dfs 裏把“祖父”節點也傳進參數裏,然後第一種情況就解決了。

那麽怎麽解決第二種呢?

註意到,如果把所有的兒子節點的權值和加在一起,然後減去每個點權值的平方和即為當前點的兒子節點們能產生的貢獻,減去一個平方和是為了除去自己和自己權值相乘的情況。

註意如何求最大值和次大值。

Code

// By YoungNeal
#include<cstdio> #include<iostream> #define N 200005 #define mod 10007 #define int long long using namespace std; int ans; int val[N]; int head[N]; int n,cnt,maxn; struct Edge{ int to,nxt; }edge[N<<1]; void add(int x,int y){ edge[++cnt].to=y; edge[cnt].nxt
=head[x]; head[x]=cnt; } void dfs(int now,int fa,int granfa){ maxn=max(maxn,val[now]*val[granfa]); ans+=val[now]*val[granfa]*2;ans%=mod; int max1=0,max2=0,sum=0,tot=0; for(int i=head[now];i;i=edge[i].nxt){ int to=edge[i].to; if(to==fa) continue; if(val[to]>val[max1]) max1=to; else if(val[to]>val[max2]) max2=to; sum+=val[to]; tot+=val[to]*val[to]; } sum*=sum; sum-=tot; ans+=sum; ans%=mod; maxn=max(maxn,val[max1]*val[max2]); for(int i=head[now];i;i=edge[i].nxt){ int to=edge[i].to; if(to==fa) continue; dfs(to,now,fa); } } signed main(){ scanf("%lld",&n); for(int x,y,i=1;i<n;i++){ scanf("%lld%lld",&x,&y); add(x,y);add(y,x); } for(int i=1;i<=n;i++) scanf("%lld",&val[i]); dfs(1,0,0); printf("%lld %lld\n",maxn,ans); return 0; }

[Noip2014] 聯合權值