1. 程式人生 > 其它 >【NOIP2014day1】聯合權值

【NOIP2014day1】聯合權值

題目

題解

首先這是棵樹,那麼任意兩點間就只有一條路徑。要想值為2,那麼就需要兩個點連到同一個點,即與同一個點直接相連

那麼我們就可以列舉中間的點,然後遍歷它所有與其相連的點。

另外總和權值可以這麼算:

假如有兩個點: 2 a b = ( a + b ) 2 − ( a 2 + b 2 ) 2ab=(a+b)^2-(a^2+b^2) 2ab=(a+b)2(a2+b2)

假如有三個點: 2 a b + 2 a c + 2 b c = ( a + b + c ) 2 − ( a 2 + b 2 + c 2 ) 2ab+2ac+2bc=(a+b+c)^2-(a^2+b^2+c^2) 2ab+2

ac+2bc=(a+b+c)2(a2+b2+c2)

… \dots

n n n個點: a n s = ( ∑ w i ) 2 − ∑ w i 2 ans=(\sum w_i)^2-\sum w_i^2 ans=(wi)2wi2

最大權值可以求出最大點權和次大點權,然後與答案比較一下就可以了

PS:只有總和要sro%orz,最大值不用sro%orz

Code

#include<cstdio>
#include<algorithm>
#define p 10007
#define ll long long
using namespace std;
struct
node { int to,head,next; }map[400005]; int n,x,y,tot; ll mx,mx1,mx2,ans,sum,a[200005]; void add(int x,int y) { map[++tot].to=y; map[tot].next=map[x].head; map[x].head=tot; } int main() { scanf("%d",&n); for (int i=1;i<n;++i) { scanf("%d%d",&x,&y); add(x,y); add
(y,x); } for (int i=1;i<=n;++i) scanf("%lld",&a[i]); for (int i=1;i<=n;++i) { mx1=0;mx2=0; sum=0; for (int j=map[i].head;j;j=map[j].next) { sum+=a[map[j].to]; ans-=a[map[j].to]*a[map[j].to]; if (a[map[j].to]>=mx1) mx2=mx1,mx1=a[map[j].to]; else if (a[map[j].to]>mx2) mx2=a[map[j].to]; } ans+=sum*sum; mx=max(mx,mx1*mx2); } printf("%lld %lld\n",mx,ans%p); return 0; }