洛谷P1351 联合权值(NOIp2014)

本文介绍了一种解决特定技巧题目的高效算法。通过避免使用效率较低的深度优先搜索(DFS),采用一次遍历结合数学公式的方法,实现了快速求解两点间权重的问题。关键在于利用数学公式简化计算过程,从而大幅度提升算法效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

技巧题

题目传送门

题目意思很简单,求两个距离为2的点的点权。可以转化为求一个点其中两条出边的点权。

刚开始写DFS,然后华丽丽地T掉了。。。以为哪里写挂了,算了一下发现是 O(n2) O ( n 2 ) 的。。。于是重新想算法

后来发现一个公式: 2(a[1]a[2]+a[2]a[3]+a[1]a[3])=(a[1]+a[2]+a[3])2(a[1]2+a[2]2+a[3]2) 2 ∗ ( a [ 1 ] ∗ a [ 2 ] + a [ 2 ] ∗ a [ 3 ] + a [ 1 ] ∗ a [ 3 ] ) = ( a [ 1 ] + a [ 2 ] + a [ 3 ] ) 2 − ( a [ 1 ] 2 + a [ 2 ] 2 + a [ 3 ] 2 )
它的拓展式也是成立的。(证明的话展开化简即可)

于是我们只需要遍历一遍就行了,算值的时候把这个东西代进去。求最大值就是求前二大值的积。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 200000
#define DALAO 10007
using namespace std;
typedef long long LL;
struct edge{
    int next,to;
};
int n,k;
LL sum,ans;
int h[MAXN+5],w[MAXN+5];
edge ed[MAXN*2+5];
inline char readc(){
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    if (l==r) return EOF; return *l++;
}
inline int _read(){
    int num=0; char ch=readc();
    while (ch<'0'||ch>'9') ch=readc();
    while (ch>='0'&&ch<='9') { num=num*10+ch-48; ch=readc(); }
    return num;
}
void addedge(int x,int y){
    ed[++k].next=h[x]; ed[k].to=y; h[x]=k;
}
void calc(int x){
    LL s1=0,s2=0,t1=0,t2=0;
    for (int i=h[x];i;i=ed[i].next){
        int v=ed[i].to;
        s1+=w[v]; s2+=w[v]*w[v];
        if (t1<w[v]) t1=w[v];
        else if (w[v]>t2) t2=w[v];
    }
    s1*=s1; ans=max(ans,t1*t2); sum=(((s1-s2)%DALAO+sum)%DALAO)%DALAO;
}
int main(){
    n=_read();
    for (int i=1;i<n;i++){
        int u=_read(),v=_read();
        addedge(u,v); addedge(v,u);
    }
    for (int i=1;i<=n;i++) w[i]=_read();
    for (int i=1;i<=n;i++) calc(i);
    printf("%lld %lld\n",ans,sum%DALAO);//其实并不需要long long
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值