传送门:http://www.sqyoj.club/problem.php?id=1171&csrf=QD68eBHQ59TuGyf7ekdpT0tILEv5bokM
三种解法:
一、注意到60%的点n<=2000,故用邻接矩阵存图,然后用三个for处理,得到60分。要注意的是两个for之后剪枝,再写第三个for,如果先写三个for,再剪枝,则只能得30分。代码如下:
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
int n,x,y,a[5705][5705],w[5705],maxx=0,ans=0;
const int MOD=10007;
int main(){
//cout<<128*1024*1024/4-5705*5705;
//freopen("link6.in","r",stdin);
cin>>n;
for(int i=1;i<n;i++){
cin>>x>>y;
a[x][y]=a[y][x]=1;
}
for(int i=1;i<=n;i++)cin>>w[i];
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(a[i][j] && i!=j)
for(int k=1;k<=n;k++)
if(i!=k && j!=k && a[i][k]){
int cns=w[j]*w[k];
maxx=max(maxx,cns);
ans=(ans+cns)%MOD;
}
cout<<maxx<<' '<<ans;
return 0;
}
二、因为100%的点n<=200000,所以用前向得存图,计算方法不改变,可以拿到70分。代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int n,num,head[200005],w[200005],maxx,ans;
const int MOD=10007;
struct Edge{
int from,to,next;
}edge[400005];
void add(int from,int to){
edge[++num].next=head[from];
edge[num].from=from;
edge[num].to=to;
head[from]=num;
}
void cal(int u){
int maxf=0,maxs=0,s=0;
for(int j=head[u];j>0;j=edge[j].next)
for(int k=head[u];k>0;k=edge[k].next)
if(j!=k){
int now1=edge[j].to,now2=edge[k].to;
int tmp=w[now1]*w[now2];
maxx=max(maxx,tmp);
ans=(ans+tmp)%MOD;
}
}
int main(){
scanf("%d",&n);
for(int x,y,i=1;i<n;++i){
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
for(int i=1;i<=n;++i)scanf("%d",&w[i]);
for(int i=1;i<=n;++i)cal(i);
printf("%d %d",maxx,ans);
return 0;
}
三、在前向星基础上,引入数学知识。
结点u的相邻结点v中用maxf,maxs表示最大与第二大的点权;
然后用公式:
w1*w2+w1*w3+w1*w4+w1*w5+...+
w2*w3+w2*w4+w2*w5+w2*w6+...+
w3*w4+w3*w5+w3*w6+w3*w7+...+
……+
w(n-1)*w(n)
=0*w1+w1*w2+(w1+w2)*w3+...+(w1+w2+...+w(n-1))*w(n)。
这样可以AC:
#include<cstdio>
using namespace std;
int n,num,head[200005],w[200005],ans1,ans2;
struct Edge{
int from,to,next;
}edge[400005];
int max(int x,int y){
return x>y?x:y;
}
void add(int from,int to){
edge[++num].next=head[from];
edge[num].from=from;
edge[num].to=to;
head[from]=num;
}
void cal(int u){
int maxf=0,maxs=0,s=0;
for(int j=head[u];j>0;j=edge[j].next){
int now=edge[j].to;//以u为起点的边的终点now
if(maxf<w[now]&&maxs<w[now]){
maxs=maxf;maxf=w[now];
}
else if(maxf>=w[now]&&maxs<w[now]){
maxs=w[now];
}
ans2=(ans2+s*w[now]%10007)%10007;s=(s+w[now])%10007;
}
ans1=max(ans1,(maxs*maxf));
}
int main(){
scanf("%d",&n);
for(int x,y,i=1;i<n;++i){
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
for(int i=1;i<=n;++i)scanf("%d",&w[i]);
for(int i=1;i<=n;++i)cal(i);
printf("%d %d",ans1,(ans2*2)%10007);
return 0;
}