[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)−w1−w2)。
由于我们是在
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+depu2−2deplca(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]情报中心的主要内容,如果未能解决你的问题,请参考以下文章