联合权值【60,70,100三种解法】

传送门: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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值