原题地址
https://www.luogu.org/problem/show?pid=1351
枚举 数论 贪心
一开始想到的又不是标解,跑了跑发现T了4个点……然后对照题解发现确实太暴力了,只有枚举连个优化都没有……
解题思路
60分算法:
枚举每个点,然后枚举和这个点相连的两个点,求联合权值的最大值,并加入和(ans)中(先乘2再加,因为(i,j)和(j,i)算两对),显然时间复杂度爆了。
100分算法:
①预处理出每个点相连的点中的最大值和次大值,二者的联合权值一定比以该点为中心的两个点的其他情况更优,然后再枚举每个点(仅有一条边相连的除外)中心点,可以求出第一问;
②用数学知识我们可以知道,若a,b,c,d,e,f都与i相连,则以i为中心的所有可行方案的联合权值之和为sum=a*b+a*c+a*d+……+e*f, 其实就是每个点的权值乘以可以联合的节点的所有权值之和, 所有权值之和,所以假设每个节点相连的所有点的权值和为sum[i],则以i为中心点的权值和为ans=a*(sum[i]-a)+b*(sum[i]-b)+……+f*(sum[i]-f),枚举每条边就可以求出最终解了。
参考代码
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
using namespace std;
const long long p=10007;
long longw[200005],r[200005],v[200005],tmp[200005];
long long max1[200005],max2[200005],sum[200005];
int num=0;
struct mc
{
intx,y,ne;
}e[400005];
void put(int x,int y)
{
num++;
e[num].x=x;
e[num].y=y;
e[num].ne=v[x];
v[x]=num;
}
int main()
{
int n;
scanf("%d",&n);
memset(r,0,sizeof(r));
intx,y;
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
put(x,y);
}
for(int i=1;i<=n;i++)
{
scanf("%d",&w[i]);
}
memset(max1,0,sizeof(max1));
memset(max2,0,sizeof(max2));
memset(sum,0,sizeof(sum));
for(inti=1;i<n;i++)
{
intx=e[i].x;
inty=e[i].y;
sum[x]+=w[y];
sum[y]+=w[x];
if(w[y]>max1[x])
{
max2[x]=max1[x];
max1[x]=w[y];
}
elseif (w[x]>max2[y]) max2[y]=w[x];
if(w[x]>max1[y])
{
max2[y]=max1[y];
max1[y]=w[x];
}
elseif (w[x]>max2[y]) max2[y]=w[x];
}
longlong mx=0,ans=0;
for(int i=1;i<=n;i++)
{
if(!max2[i]) continue;
if(max1[i]*max2[i]>mx) mx=max1[i]*max2[i];
}
for(int i=1;i<n;i++)
{
intx=e[i].x;
inty=e[i].y;
ans=(ans+(sum[x]-w[y])*w[y]%p)%p;
ans=(ans+(sum[y]-w[x])*w[x]%p)%p;
}
cout<<mx<<''<<ans;
return0;
}