[NOI Online 2022]如何正确地排序
Posted StaroForgin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[NOI Online 2022]如何正确地排序相关的知识,希望对你有一定的参考价值。
如何正确地排序
题解
首先关注这个
f
(
i
,
j
)
=
min
k
=
1
m
(
a
k
,
i
+
a
k
,
j
)
+
max
k
=
1
m
(
a
k
,
i
+
a
k
,
j
)
f(i,j)=\\min_k=1^m(a_k,i+a_k,j)+\\max_k=1^m(a_k,i+a_k,j)
f(i,j)=mink=1m(ak,i+ak,j)+maxk=1m(ak,i+ak,j),显然我们可以将
min
\\min
min与
max
\\max
max分开计算。
我们要算的是二元对
(
i
,
j
)
(i,j)
(i,j)的最小值,这东西怎么搞。
不如就对于每个
k
k
k,求出
∑
i
=
1
n
∑
j
=
1
n
[
min
k
′
≠
k
(
a
k
′
,
i
+
a
k
′
,
j
)
⩾
a
k
,
i
+
a
k
,
j
]
(
a
k
,
i
+
a
k
,
j
)
\\sum_i=1^n\\sum_j=1^n[\\min_k'\\not = k(a_k',i+a_k',j)\\geqslant a_k,i+a_k,j](a_k,i+a_k,j)
∑i=1n∑j=1n[mink′=k(ak′,i+ak′,j)⩾ak,i+ak,j](ak,i+ak,j),也就是算它在所有它最优的地方的最优值的和。
如果值最优,那么有
a
k
,
i
+
a
k
,
j
⩽
a
k
′
,
i
+
a
l
′
,
j
⇒
(
a
k
,
i
−
a
k
′
,
i
)
+
(
a
k
,
j
−
a
k
′
,
j
)
⩽
0
a_k,i+a_k,j\\leqslant a_k',i+a_l',j\\Rightarrow (a_k,i-a_k',i)+(a_k,j-a_k',j)\\leqslant 0
ak,i+ak,j⩽ak′,i+al′,j⇒(ak,i−ak′,i)+(ak,j−ak′,j)⩽0,转化到差值上面去。
既然要两个差值之和不超过
0
0
0,那么显然,我们可以在枚举一个
i
i
i的同时,看看有哪些
j
j
j可以匹配到它们使其最优。
显然,我们有
m
m
m个数列,如果它最优的话,也就是说也要超过其他
m
−
1
m-1
m−1个数列,相当于要满足其他
m
−
1
m-1
m−1个不等式,这不是个
m
−
1
m-1
m−1维的偏序问题吗。我们的
m
m
m最大为
3
3
3,显然不太可能直接用什么kd树,3维树套树解决。
但这个问题并不是需要在线回答,我们不妨先将一维排序后边加入边询问。
如果我们按差值从大到小,那么显然,显然每个合法的
i
i
i对应的
j
j
j区间都是一个后缀,随
i
i
i的增大,后缀的又右边界左移。
显然,这是一个指针移动的过程,我们可以只要排序后用指针维护一维,就只用一个二维树套树维护另外的两维了,这样就可以将时间复杂度变成
O
(
n
m
log
2
n
)
O\\left(nm\\log^2n\\right)
O(nmlog2n)。
但很可惜的是,这个时间复杂度还不足以通过我们的数据,它只能拿到
70
p
t
s
70pts
70pts。
我们可以考虑用其它的方法解决我们这个问题。
我们有一种很好的计算最大值或者最小值的方法,它叫作容斥。
与
min
_
max
\\min\\_\\max
min_max类似,我们考虑如何容斥可以得到我们的最小值。
显然,我们的先加上每个大小为
1
1
1子集的最小值,现在每个非最小值都多一,我们再给每个大小为
2
2
2的子集的最大值减一,那最大的就变为
−
2
-2
−2,次大的变为
−
1
-1
−1,次小的变为
0
0
0,最小的变为
1
1
1。
再给每个大小为
3
3
3的子集的最大值加
1
1
1,就只剩下最大值多
1
1
1了,将它减去即可。
我们现在要算为
1
1
1的集合
4
4
4次,为
2
2
2的集合
6
6
6次,为
3
3
3的集合
4
4
4次,为
4
4
4的集合。
又有为
4
4
4的集合,好像并没有优化什么。
ちょっと待って,我们好像最后减去了一个所有点的最大值,但我们要求的是和呀。
如果我们不减这个,不就直接得到答案了吗,过程中我们所有需要求的大小最多为
3
3
3,而为
3
3
3的话我们就没必要树套树了。
这样的话,我们就可以将时间复杂度降到
O
(
n
2
m
log
2
n
)
O\\left(n2^m\\log^2 n\\right)
O(n2mlog2n),简单地通过此题。
当然注意一下,中途是可能出现有两个数列的和相同的情况,这种情况,我们加上一个 ( i < j ) (i<j) (i<j)规定顺序即可。
源码
#include<bits/stdc++.h>
using namespace std;
#define MAXN 200005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
typedef long double Ld;
typedef pair<LL,int> pli;
typedef pair<int,int> pii;
const LL INF=0x3f3f3f3f;
const int mo=998244353;
const int mod=1e6+7;
const int inv2=499122177;
const int inv3=332748118;
const int jzm=2333;
const int zero=2000;
const int n1=100;
const int lim=200000;
const int orG=3,ivG=332748118;
const double Pi=acos(-1.0);
const double feps=1e-11;
const double eps=1e-9;
template<typename _T>
_T Fabs(_T x)return x<0?-x:x;
template<typename _T>
void read(_T &x)
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0')if(s=='-')f=-1;s=getchar();
while('0'<=s&&s<='9')x=(以上是关于[NOI Online 2022]如何正确地排序的主要内容,如果未能解决你的问题,请参考以下文章