[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=1nj=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,jak,i+al,j(ak,iak,i)+(ak,jak,j)0,转化到差值上面去。
既然要两个差值之和不超过 0 0 0,那么显然,我们可以在枚举一个 i i i的同时,看看有哪些 j j j可以匹配到它们使其最优。
显然,我们有 m m m个数列,如果它最优的话,也就是说也要超过其他 m − 1 m-1 m1个数列,相当于要满足其他 m − 1 m-1 m1个不等式,这不是个 m − 1 m-1 m1维的偏序问题吗。我们的 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]如何正确地排序的主要内容,如果未能解决你的问题,请参考以下文章

noi online 提高组t2

[Noi2016]区间

noi online round2(入门组)

第三次noi online爆零记

第三次noi online爆零记

P6185 [NOI Online #1 提高组] 序列(二分图)