当前位置: 代码迷 >> 综合 >> 【题解】洛谷 P2296 [NOIP2014 提高组] 寻找道路
  详细解决方案

【题解】洛谷 P2296 [NOIP2014 提高组] 寻找道路

热度:16   发布时间:2024-01-06 07:27:45.0

题目传送门

题意

无向连通图 G G G n n n 个点, n ? 1 n-1 n?1 条边。点从 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 u × W v W_u\times W_v Wu?×Wv? 的联合权值。

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

题解

既然“联合”的两个点距离为 2 2 2,那么它们之间必然有一个中间点。我们可以通过枚举这个中间点来枚举所有的联合距离。

对于一个节点,它的度为 n n n,那么以它为中间点的联合权值的总和 s s s
2 ∑ i = 1 n ? 1 ∑ j = i + 1 n W i W j 2\sum_{i=1}^{n-1}\sum_{j=i+1}^nW_iW_j 2i=1n?1?j=i+1n?Wi?Wj?
一定要记住,前面有个 2 2 2,我一开始就是被这个给坑了。

双重和式,硬算复杂度 O ( n 2 ) O(n^2) O(n2),不 TLE 才怪。
所以我们只好魔改一下这个式子。众所周知:
1 2 s = ∑ i = 1 n ? 1 ∑ j = i + 1 n W i W j = W 1 W 2 + W 1 W 3 + . . . + W 1 W n + W 2 W 3 + W 2 W 4 + . . . + W 1 W n + . . . + W n ? 1 W n \begin{aligned}\frac{1}{2}s&=\sum_{i=1}^{n-1}\sum_{j=i+1}^nW_iW_j\\&=W_1W_2+W_1W_3+...+W_1W_n+\\&\ \ \ \ \ W_2W_3+W_2W_4+...+ W_1W_n+\\&\ \ \ \ \ ...+\\&\ \ \ \ \ W_{n-1}W_n\end{aligned} 21?s?=i=1n?1?j=i+1n?Wi?Wj?=W1?W2?+W1?W3?+...+W1?Wn?+     W2?W3?+W2?W4?+...+W1?Wn?+     ...+     Wn?1?Wn??
然后突然想到了这么一个式子:
( ∑ i = 1 n W i ) 2 = W 1 2 + W 1 W 2 + . . . + W 1 W n + W 1 W 2 + W 2 2 + . . . + W 2 W n + . . . + W 1 W n + W 2 W n + . . . + W n 2 \begin{aligned}\Bigg(\sum_{i=1}^nW_i\Bigg)^2&=W_1^2+W_1W_2+...+W_1W_n+\\&\ \ \ \ \ W_1W_2+W_2^2+...+W_2W_n+\\&\ \ \ \ \ ...+\\&\ \ \ \ \ W_1W_n+W_2W_n+...+W_n^2\end{aligned} (i=1n?Wi?)2?=W12?+W1?W2?+...+W1?Wn?+     W1?W2?+W22?+...+W2?Wn?+     ...+     W1?Wn?+W2?Wn?+...+Wn2??
注意到它的主对角线,分别是 W 1 2 W_1^2 W12? W 2 2 W_2^2 W22?、……、 W n 2 W_n^2 Wn2?。也就是说,主对角线上的数都是 W i 2 W_i^2 Wi2? 形式的。再看到主对角线的上下,居然发现各是一个 1 2 s \frac{1}{2}s 21?s!所以说,把整个式子减去主对角线上的数的和再除以 2 2 2 就可以得到 1 2 s \frac{1}{2}s 21?s,再乘回 2 2 2 就变成了 s s s。写得学术一点,就是
s = ( ∑ i = 1 n W i ) 2 ? ∑ i = 1 n W i 2 s=\Bigg(\sum_{i=1}^nW_i\Bigg)^2-\sum_{i=1}^nW_i^2 s=(i=1n?Wi?)2?i=1n?Wi2?
双重和式就这么被我们拆成了两个单独的和式,计算这个式子的复杂度为 O ( n ) O(n) O(n),可以过了。

然后就是最大值的计算。注意不用取模!对于一个中间点,它的最大联合权值就是点权第一大的与点权第二大的乘积。这个很好计算,具体细节参考代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=200005,inf=0x7fffffff,mod=10007;
vector<int> a[maxn];//邻接表存图
int w[maxn];
int main()
{
    int n;scanf("%d",&n);for(int i=1;i<n;i++){
    int x,y;scanf("%d%d",&x,&y);a[x].push_back(y);a[y].push_back(x);}for(int i=1;i<=n;i++) scanf("%d",&w[i]);int tot=0,ans=0;for(int i=1;i<=n;i++){
    if(a[i].size()<2) continue;int x=0,y=0,maxxx=0,maxx=0;//x计算和的平方,y计算平方的和,maxx是最大的权值,maxxx是第二大的权值for(int j=0;j<a[i].size();j++){
    int k=a[i][j];x=(x+w[k])%mod;y=(y+w[k]*w[k])%mod;if(maxx<w[k])//比最大值还大{
    maxxx=maxx;//原最大值变成次大值maxx=w[k];//最大值更新}else if(maxxx<w[k]) maxxx=w[k];//直接更新次大值}tot=(tot+mod+x*x%mod-y)%mod;ans=max(ans,maxxx*maxx);}printf("%d %d",ans,tot%mod);return 0;
}