cogs1804 联合权值 dp

填坑……链接:http://cogs.pro/cogs/problem/problem.php?pid=1804

题意:给出一棵树,定义两个距离为2的点权值乘积为这两个点产生的联合权值,求出整棵树最大联合权值及联合权值之和。

实际上早就做过……现在重做旧题感觉还是很有必要……

我们可以枚举每一个点作为可以产生联合权值的两点的中点,对于这个点来说,他周围的点权值之和就是

\sum\limits_{i=1}^N(\sum\limits_{u,v \in adj_i, u \neq v} W_u \times W_v)

然后对于每一个中点,他做出的贡献也就可以表示成

S_i = \sum_{j \in adj_i} (W_j \times (\sum_{k \in adj_i} W_k) - W_j)

然后大力化简可得

S_i ={ (\sum_{j \in adj_i} W_j)} ^ 2 - \sum_{j \in adj_i} W_j^2

这也就是我们需要的求和式子。

至于最大的嘛……直接找到最大的和次大的相乘比较就可以了……

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 using namespace std;
 6 const int mod=10007,maxn=200005;
 7 struct node
 8 {
 9     int from,to,next;
10 }edge[maxn<<1];
11 int head[maxn],tot,n,weight[maxn];
12 void addedge(int u,int v)
13 {
14     edge[++tot]=(node){u,v,head[u]};head[u]=tot;
15 }
16 int haha()
17 {
18     freopen("linkb.in","r",stdin);
19     freopen("linkb.out","w",stdout);
20     scanf("%d",&n);
21     for(int i=1;i<n;i++)
22     {
23         int x,y;scanf("%d%d",&x,&y);
24         addedge(x,y);addedge(y,x);
25     }
26     for(int i=1;i<=n;i++)scanf("%d",&weight[i]);
27     int maxx=0;long long sum=0;
28     for(int i=1;i<=n;i++)
29     {
30         int firmax=0,secmax=0;long long sigma=0,pow2sigma=0;
31         for(int j=head[i];j;j=edge[j].next)
32         {
33             int v=edge[j].to;
34             if(weight[v]>firmax)secmax=firmax,firmax=weight[v];
35             else secmax=max(secmax,weight[v]);
36             sigma=(sigma+weight[v]);pow2sigma=(pow2sigma+weight[v]*weight[v])%mod;
37         }
38         maxx=max(maxx,firmax*secmax);
39         sum=(sum+sigma*sigma-pow2sigma)%mod;
40     }
41     printf("%d %lld",maxx,sum);
42 }
43 int sb=haha();
44 int main(){;}
cogs1804

 

posted @ 2017-08-14 13:11  ccc000111  阅读(163)  评论(0编辑  收藏  举报