[Luogu 1351] NOIP2014 联合权值

[Luogu 1351] NOIP2014 联合权值


存图,对于每一个点 \(u\),遍历它的所有邻接点。以 \(u\) 为中转点的点对中,\((x,y)\) 的联合权值 \(w_x \cdot w_y\) 最大,当且仅当 \(x\)\(y\) 的点权在 \(u\) 的所有邻接点中是前两大的。

成功尝试内嵌 HTML 控制背景色,开心。

每遍历一个点 \(v\),它对联合权值之和 \(\mathrm{sum}\) 的贡献,等于其点权 \(w_v\) 乘目前已遍历点的点权和 \(\mathrm{NodeSum}\) 再乘 \(2\),即 \(\mathrm{sum} += 2w_v \cdot \mathrm{NodeSum}\)。(取模省略,代码中体现)

然后一边求 \(u\) 的邻接点中的最大点权 \(\mathrm{max1}\) 和次大点权 \(\mathrm{max2}\),一边更新 \(\mathrm{NodeSum}\)(教练跟我说更新这个的过程叫什么前序和优化)。

\(u\) 的邻接点遍历完毕后,\(\mathrm{sum}\) 也更新完毕了。至于最大联合权值 \(\mathrm{ans}\),比较当前 \(\mathrm{ans}\) 与此次遍历求出的 \(\mathrm{max1} \cdot \mathrm{max2}\),更新最大值即可。

最终输出 \(\mathrm{ans}\)\(\mathrm{sum}\) 即可。

#include <algorithm>
#include <cstdio>
using std::max;
const int MAXN=2000010,P=10007;
int n,ans,sum,w[MAXN];
struct Edge
{
    int to;
    Edge *next;
    Edge(int to,Edge* next):to(to),next(next){}
    ~Edge(void)
    {
        if(next!=nullptr)
            delete next;
    }
}*head[MAXN];
void Initialize(void)
{
    for(int i=1;i<=n;++i)
        head[i]=nullptr;
}
void AddEdges(int u,int v)
{
    head[u]=new Edge(v,head[u]);
    head[v]=new Edge(u,head[v]);
}
void Solve(int u)
{
    int NodeSum=0,max1=0,max2=0;
    for(Edge *i=head[u];i!=nullptr;i=i->next)
    {
        int v=i->to;
        sum=(sum+(NodeSum*w[v]<<1))%P;
        if(max1<w[v])
        {
            max2=max1;
            max1=w[v];
        }
        else
            max2=max(max2,w[v]);
        NodeSum=(NodeSum+w[v])%P;
    }
    ans=max(ans,max1*max2);
}
void Destroy(void)
{
    for(int i=1;i<=n;++i)
        delete head[i];
}
int main(int argc,char** argv)
{
    scanf("%d",&n);
    for(int i=1,u,v;i<n;++i)
    {
        scanf("%d %d",&u,&v);
        AddEdges(u,v);
    }
    for(int i=1;i<=n;++i)
        scanf("%d",&w[i]);
    for(int i=1;i<=n;++i)
        Solve(i);
    printf("%d %d\n",ans,sum);
    return 0;
}

谢谢阅读。

转载于:https://www.cnblogs.com/Capella/p/9135482.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值