这道题其实重点在于抓住一个细节,每条边的长度均为1,若它们的距离为2。
这代表着啥?
就是说,两两结点的联合权值,必定经过另外一个结点。所以说,枚举的时候,只需要枚举每一个结点以及与这个节点相邻的点。然后把这些结点两两配对相乘相加,就Ok了。可是,这样还是抄了。。。。怎么再优化?
我们再次分析一下。对于最大值,明显只需要贪心。在每个结点中选择最大的两个即可。这个不需要考虑。那么,总和值是否可以优化呢?假设一个结点o,与其相连结点的权值为a1,a2,a3,a4……ai,那么,答案sum=(a1*a2+a1*a3+……a1*ai)+(a2*a1+a2*a3+……a2*ai)+……+(ai*a1+ai*a2+……ai*ai-1)这个时候,再进行乘法结合律,我们发现,其实就是这些结点中,该结点的权值乘上所有结点权值减去他自己的权值。比较绕口,写成表达式就是aj*(sum[o]-aj)其中sum[o]表示的是与o相连的所有结点的权值之和。这样,预处理一下就Ok啦。算法的时间复杂度应该是O(kn),k应该是一个远远小于n的数吧
贴代码:
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<vector>
#define mod 10007
using namespace std;
vector<int>a[200001];
int w[200001];
int sum[200001];
int main()
{
intn;
cin>>n;
for(inti=1;i<=n-1;i++)
{
intx,y;
scanf("%d%d",&x,&y);
a[x].push_back(y);
a[y].push_back(x);
}
for(inti=1;i<=n;i++) cin>>w[i];
for(inti=1;i<=n;i++)
{
for(intj=0;j<a[i].size();j++)
{
sum[i]+=w[a[i][j]];
sum[i]%=mod;
}
}
intans=0,maxn=0;
for(inti=1;i<=n;i++)
if(a[i].size()!=1)
{
intmax1=w[a[i][0]];
intmax2=w[a[i][1]];
ans+=max1*(sum[i]-max1)+max2*(sum[i]-max2);
ans%=mod;
for(intj=2;j<a[i].size();j++)
{
intkk=w[a[i][j]];
inttt=kk;
if(tt>max1){swap(tt,max1);swap(tt,max2);}
elseif(tt>max2) {swap(tt,max2);}
ans+=kk*(sum[i]-kk);
ans%=mod;
}
maxn=max(maxn,max1*max2);
}
printf("%d%d\n",maxn,ans);
return0;
}