欢迎光临
我们一直在努力

P1351 联合权值【洛谷算法习题】

P1351 联合权值

网页链接

P1351 联合权值 在这里插入图片描述

题目背景

NOIP2014 提高组 D1T2

题目描述

无向连通图

G

G

G

n

n

n 个点,

n

1

n-1

n1 条边。点从

1

1

1

n

n

n 依次编号,编号为

i

i

i 的点的权值为

W

i

W_i

Wi,每条边的长度均为

1

1

1。图上两点

(

u

,

v

)

(u, v)

(u,v) 的距离定义为

u

u

u 点到

v

v

v 点的最短距离。对于图

G

G

G 上的点对

(

u

,

v

)

(u, v)

(u,v),若它们的距离为

2

2

2,则它们之间会产生

W

v

×

W

u

W_v \\times W_u

Wv×Wu 的联合权值。

请问图

G

G

G 上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少?

输入格式

第一行包含

1

1

1 个整数

n

n

n

接下来

n

1

n-1

n1 行,每行包含

2

2

2 个用空格隔开的正整数

u

,

v

u,v

u,v,表示编号为

u

u

u 和编号为

v

v

v 的点之间有边相连。

最后

1

1

1 行,包含

n

n

n 个正整数,每两个正整数之间用一个空格隔开,其中第

i

i

i 个整数表示图

G

G

G 上编号为

i

i

i 的点的权值为

W

i

W_i

Wi

输出格式

输出共

1

1

1 行,包含

2

2

2 个整数,之间用一个空格隔开,依次为图

G

G

G 上联合权值的最大值和所有联合权值之和。由于所有联合权值之和可能很大,输出它时要对

10007

10007

10007 取余。

输入输出样例 #1

输入 #1

5
1 2
2 3
3 4
4 5
1 5 2 3 10

输出 #1

20 74

说明/提示

样例解释

本例输入的图如上所示,距离为

2

2

2 的有序点对有

(

1

,

3

)

(1,3)

(1,3)

(

2

,

4

)

(2,4)

(2,4)

(

3

,

1

)

(3,1)

(3,1) 、$(3,5)

(4,2)$ 、$(5,3) $。

其联合权值分别为

2

,

15

,

2

,

20

,

15

,

20

2,15,2,20,15,20

2,15,2,20,15,20。其中最大的是

20

20

20,总和为

74

74

74

数据说明

  • 对于

    30

    %

    30\\%

    30% 的数据,

    2

    <

    n

    100

    2 < n \\leq 100

    2<n100

  • 对于

    60

    %

    60\\%

    60% 的数据,

    2

    <

    n

    2000

    2 < n \\leq 2000

    2<n2000

  • 对于

    100

    %

    100\\%

    100% 的数据,

    2

    <

    n

    2

    ×

    10

    5

    2 < n \\leq 2\\times 10^5

    2<n2×105

    0

    <

    W

    i

    10000

    0 < W_i \\leq 10000

    0<Wi10000

保证一定存在可产生联合权值的有序点对。

解题思路

本题是树上距离为2的点对统计问题,核心思路是枚举中间节点 + 数学公式优化,将复杂度从暴力的平方级降至线性,适配十万级数据规模。

树中任意距离为2的点对,必然存在且仅存在一个公共邻接点(中间节点)。因此我们可以枚举每个节点作为中间点,统计其所有邻接点两两之间产生的联合权值,分别累加总和与更新最大值:

  • 联合权值总和:对于中间节点

    u

    u

    u,设其邻接点权值为

    w

    1

    ,

    w

    2

    ,

    ,

    w

    k

    w_1,w_2,\\dots,w_k

    w1,w2,,wk。所有有序点对的乘积和可由平方和公式推导:

    i

    j

    w

    i

    w

    j

    =

    (

    i

    =

    1

    k

    w

    i

    )

    2

    i

    =

    1

    k

    w

    i

    2

    \\sum_{i \\neq j} w_i w_j = \\left(\\sum_{i=1}^k w_i\\right)^2 – \\sum_{i=1}^k w_i^2

    i=jwiwj=(i=1kwi)2i=1kwi2 只需遍历一次邻接点,维护权值和与平方和,即可在

    O

    (

    k

    )

    O(k)

    O(k) 时间内算出该节点贡献的总权值,无需两两枚举。

  • 最大联合权值:对于中间节点

    u

    u

    u,邻接点中权值最大的两个数的乘积,就是以

    u

    u

    u 为中间点的最大联合权值。遍历邻接点时维护最大值与次大值,相乘后更新全局最大值即可。

  • 最终遍历所有节点后,全局最大值即为答案的第一部分,所有节点贡献的权值和取模后即为答案的第二部分。算法总时间复杂度为

    O

    (

    n

    )

    O(n)

    O(n),因为树的总边数为

    n

    1

    n-1

    n1,所有节点的度数之和为

    2

    (

    n

    1

    )

    2(n-1)

    2(n1),遍历无冗余。

    总结

    核心逻辑:将距离为2的点对转化为共享中间节点的邻接点对,通过数学公式与最值维护线性求解总和与最大值。 关键操作:链式前向星存树、平方和公式计算总权值、维护最大次大权值更新全局最大值、全程取模处理总和。 效率保障:仅一次遍历所有节点与边,无嵌套循环,线性复杂度完美适配二十万节点的数据规模。

    代码简要说明

  • 图存储:使用链式前向星存储无向树,每条边双向添加,适配树的邻接遍历。
  • 逐节点处理:遍历每个节点作为中间点,初始化最大值max1、次大值max2、权值和t1、平方和t2。
  • 邻接点遍历:遍历当前节点的所有邻接点,更新最大/次大权值,同时累加权值和与平方和,过程中对和与平方和取模。
  • 更新全局结果:用max1 * max2更新全局最大联合权值;用和的平方 – 平方和计算当前节点贡献的总权值,加到全局总和中,减法后加模数避免负数再取模。
  • 输出结果:最终输出全局最大值与取模后的总权值和。
  • 代码内容

    #include <bits/stdc++.h>
    using namespace std;

    #define endl '\\n'
    typedef long long ll;
    typedef unsigned long long ull;
    typedef vector<vector<ll>> vvt;
    typedef pair<ll,ll> pll;
    const ll N=1e3+10;
    const ll INF=1e18;
    const ll M=1e6+10;
    const ll mod=1e9+7;

    struct edge
    {
    ll next;
    ll to;
    }a[400005];
    ll edgenum,head[200005],w[200005];
    ll n,ans,maxx;

    void add(ll u,ll v)
    {
    a[++edgenum].next=head[u];
    a[edgenum].to=v;
    head[u]=edgenum;
    }

    int main()
    {
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    scanf("%lld",&n);
    for(ll i=1;i<n;i++)
    {
    ll u,v;
    scanf("%lld%lld",&u,&v);
    add(u,v);
    add(v,u);
    }
    for(ll i=1;i<=n;i++) scanf("%lld",&w[i]);
    for(ll i=1;i<=n;i++)
    {
    ll max1=0,max2=0;
    ll t1=0,t2=0;
    for(ll j=head[i];j;j=a[j].next)
    {
    if(w[a[j].to]>max1){max2=max1; max1=w[a[j].to];}
    else if(w[a[j].to]>max2) max2=w[a[j].to];
    t1=(t1+w[a[j].to])%10007;
    t2=(t2+w[a[j].to]*w[a[j].to])%10007;
    }
    t1=t1*t1%10007;
    ans=(ans+t1+10007t2)%10007;
    if(maxx<max1*max2) maxx=max1*max2;
    }
    printf("%lld %lld\\n",maxx,ans);
    return 0;
    }

    赞(0)
    未经允许不得转载:171主机测评 » P1351 联合权值【洛谷算法习题】
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址