[NOI2018]情报中心

Posted StaroForgin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[NOI2018]情报中心相关的知识,希望对你有一定的参考价值。

情报中心

题解

首先,让我们先想想,如果两条路径相交的话,它们应该是长什么样子的。
大概是这样的:

也就是说,它们具有两个分割点,也就是相交路径的两端。
显然,肯定存在一个分割点是两条路径在它那边端点的 l c a lca lca
我们不妨尝试在这个点上统计这两条路径的贡献。

但这贡献好像不是很好表示的样子呀,它重复的部分只会产生一次的贡献呀。
没事,我们不是有它的两个分割点吗?把两个分割点外的部分多统计一次,再除个 2 2 2,中间就只会贡献一次了。
记这两条路径分别为 ( u 1 , v 1 , w 1 ) , ( u 2 , v 2 , w 2 ) (u_1,v_1,w_1),(u_2,v_2,w_2) (u1,v1,w1),(u2,v2,w2),其中 u 1 u_1 u1 u 2 u_2 u2相邻, v 1 v_1 v1 v 2 v_2 v2相邻。
显然,我们的贡献为 1 2 ( d i s ( u 1 , v 1 ) + d i s ( u 2 , v 2 ) + d i s ( u 1 , u 2 ) + d i s ( v 1 , v 2 ) − w 1 − w 2 ) \\frac12(dis(u_1,v_1)+dis(u_2,v_2)+dis(u_1,u_2)+dis(v_1,v_2)-w_1-w_2) 21(dis(u1,v1)+dis(u2,v2)+dis(u1,u2)+dis(v1,v2)w1w2)
由于我们是在 u 1 , u 2 u_1,u_2 u1,u2 l c a lca lca统计的答案,所以可以把 d i s ( u 1 , u 2 ) dis(u_1,u_2) dis(u1,u2)拆成 d e p u 1 + d e p u 2 − 2 d e p l c a ( u 1 , u 2 ) dep_u_1+dep_u_2-2dep_lca(u_1,u_2) depu1+depu22deplca(u1,u2)
这样的话,我们就是要最小化 ( d e p u 1 + d i s ( u 1 , v 1 ) − w 1 ) + ( d e p u 2 + d i s ( u 2 , v 2 ) − w 2 ) + d i s ( v 1 , v 2 ) (dep_u_1+dis(u_1,v_1)-w_1)+(dep_u_2+dis(u_2,v_2)-w_2)+dis(v_1,v_2) (depu1+dis(u1,v1)w1)+(depu2+dis(u2,v2)w2)+dis(v1,v2)
前面这两个东西分别与这两条路径各自相关,而涉及到它们两者的就只剩下后面这个 d i s ( v 1 , v 2 ) dis(v_1,v_2) dis(v1,v2)了。
我们完全可以把两条路径的贡献插在 v 1 , v 2 v_1,v_2 v1,v2上,这样每次就只需要在子树的外边询问一个带权最远点对。

带权最远点对好维护,我们记 d i a m ( S ) diam(S) diam(S)表示集合 S S S内的最远点对,显然有, d i a m ( A + B ) = d i a m ( d i a m ( A ) + d i a m ( B ) ) diam(A+B)=diam(diam(A)+diam(B)) diam(A+B)=diam(diam(A)+diam(B))
也就是说,我们可以采用 d f s dfs dfs序线段树的形式,在每个节点上维护 d f s dfs dfs序在这个区间内的点的带权最远点对。
往上 p u s h u p pushup pushup的时候,只需要合并两个子区间的最远点对即可。
当然,维护最远点对的合并显然就要块数求出两点之间的距离,这也就意味着我们还得打一个 O ( 1 ) O(1) O(1) l c a lca lca
当然,我们要让两条路径刚好在 l c a lca lca上统计,所以其实是在线段树合并的时候统计的两个集合之间贡献的答案。

时间复杂化度 O ( m log ⁡ n ) O\\left(m\\log n\\right) O(mlogn)

源码

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
#define MAXN 100005
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
const LL INF=0x3f3f3f3f3f3f3f3f;
const int mo=998244353;
template<typename _T>
void read(_T &x)
    _T f=1;x=0;char s=getchar();
    while(s<'0'||s>'9')if(s=='-')f=-1;s=getchar();
    while('0'<=s&&s<='9')x=(x<<3)+(x<<1)+(s^48);s=getchar();
    x*=f;

template<typename _T>
_T Fabs(_T x)return x<0?-x:x;
int add(int x,int y,int p)return x+y<p?x+y:x+y-p;
以上是关于[NOI2018]情报中心的主要内容,如果未能解决你的问题,请参考以下文章

[NOI2018]情报中心

noi2018 情报中心

Noi2018 归途

NOI2018 模拟 T2

bzoj1500 [NOI2005]维修数列

BZOJ 4196: [Noi2015]软件包管理器