1. 程式人生 > >洛谷P1351 聯合權值(NOIp2014)

洛谷P1351 聯合權值(NOIp2014)

技巧題

題目傳送門

題目意思很簡單,求兩個距離為2的點的點權。可以轉化為求一個點其中兩條出邊的點權。

剛開始寫DFS,然後華麗麗地T掉了。。。以為哪裡寫掛了,算了一下發現是O(n2)的。。。於是重新想演算法

後來發現一個公式:2(a[1]a[2]+a[2]a[3]+a[1]a[3])=(a[1]+a[2]+a[3])2(a[1]2+a[2]2+a[3]2)
它的拓展式也是成立的。(證明的話展開化簡即可)

於是我們只需要遍歷一遍就行了,算值的時候把這個東西代進去。求最大值就是求前二大值的積。

程式碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 200000
#define DALAO 10007
using namespace std;
typedef long long LL;
struct edge{
    int next,to;
};
int n,k;
LL sum,ans;
int h[MAXN+5],w[MAXN+5];
edge ed[MAXN*2+5];
inline char readc(){
    static char buf[100000],*l=buf,*r=buf;
    if
(l==r) r=(l=buf)+fread(buf,1,100000,stdin); if (l==r) return EOF; return *l++; } inline int _read(){ int num=0; char ch=readc(); while (ch<'0'||ch>'9') ch=readc(); while (ch>='0'&&ch<='9') { num=num*10+ch-48; ch=readc(); } return num; } void addedge(int x,int y){ ed[++k].next=h[x]; ed[k].to=y; h[x]=k; } void
calc(int x){ LL s1=0,s2=0,t1=0,t2=0; for (int i=h[x];i;i=ed[i].next){ int v=ed[i].to; s1+=w[v]; s2+=w[v]*w[v]; if (t1<w[v]) t1=w[v]; else if (w[v]>t2) t2=w[v]; } s1*=s1; ans=max(ans,t1*t2); sum=(((s1-s2)%DALAO+sum)%DALAO)%DALAO; } int main(){ n=_read(); for (int i=1;i<n;i++){ int u=_read(),v=_read(); addedge(u,v); addedge(v,u); } for (int i=1;i<=n;i++) w[i]=_read(); for (int i=1;i<=n;i++) calc(i); printf("%lld %lld\n",ans,sum%DALAO);//其實並不需要long long return 0; }