「CF1637H」Minimize Inversions Number
题目
点这里看题目。
给定一个 的排列 。
你可以进行下列操作正好一次:
- 选定 的一个长度为 的子序列,并将其按照相同的顺序移动到 的最前面。
对于 ,分别求出 在操作后的最小逆序对数。
每个测试点包含 组数据。所有数据满足 。
分析
评价:6。
首先尝试将操作后的逆序对数写出来。设 ,设 为选中的要移动的子序列的下标。则容易发现,仅有 这样的 会影响逆序对数。具体地,操作前后逆序对变化量为:
这是因为这样的 会被反序,所以原顺序对贡献 ,原逆序对贡献 。其它的 对的顺序则保持不变。
改写此式,去掉 。想到设 ,那么:
设 。因为存在 这一项,所以我们即便写出来了 的算式,这个问题也并没有变简单。我们接下来还是要考察最优的 的性质。
尝试对于 进行调整。想要让 不变大,则调整至少要满足以下几个之一:
-
变小。(但是控制逆序对其实不很容易)
-
变小,也就是把某个 变小。
-
变大。
这一部分可能就没什么处理技术了,只能手动试一试。
例如,如果要让 变大的话,可以考虑将 替换为某个满足 的下标 (因为这样 替换为 后 一定变大)。为了便于分析,我们可以钦定 为满足此条件的最小的一个。那么,分析 的变化:
-
考虑 的部分的“贡献”:
-
若 ,no change。
-
若 ,则 会变大,而 有可能变大,不过这并不影响它给 带来的贡献为非正。
-
若 ,no change。
-
-
考虑 的贡献:根据 ,它自带 贡献。其次 会增大 。所以总贡献为负。
-
考虑 的部分的“贡献”。首先,根据 ,它们会自带一个 的贡献。其次:
-
若 ,则 。此时 一定会减去 ,而 可能会增大 ,总的来说贡献肯定为负。
-
若 ,则 一定会增大 。如果 ,则 还会增大 。
看起来贡献好像是正的,但是我们可以重新选取 使得不存在这样的 ,那么就不必考虑这种情况了。
-
-
考虑 的部分的“贡献”:此时只有 可能变化,显然 只会变小。
(其它的方向我没有尝试过 😦)
这一部分分析可以推导出如下结论:一定存在一种最优的 ,满足若 ,则 , 也 。
仔细想想,这个结论可以帮助我们消去 。设 ,则可以发现 。而结论告诉我们,一定存在最优解使得等号成立,于是我们可以直接用较松的形式:
容易发现,现在这个问题其实可以用“最小权闭合子图”的方法做。这也暗示了另一点:虽然上述结论限制了 的形态,但是它的限制并不强,以至于可能的 依然有很多种。
于是,进一步研究 的性质。尝试继续在已有的经验上考虑:如果有 ,那 的关系如何?类似于前面“摊开贡献”的分析,我们又可以发现 。
结合上述结论,我们发现,一种最优的 就是按照 从小到大取 个。于是就可以 解决了。
代码
#include <bits/stdc++.h>
#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
typedef long long LL;
const int MAXN = 5e5 + 5;
template<typename _T>
inline void Read( _T &x ) {
x = 0; char s = getchar(); bool f = false;
while( s < '0' || '9' < s ) { f = s == '-', s = getchar(); }
while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
if( f ) x = -x;
}
template<typename _T>
inline void Write( _T x ) {
if( x < 0 ) putchar( '-' ), x = -x;
if( 9 < x ) Write( x / 10 );
putchar( x % 10 + '0' );
}
int perm[MAXN], cnt[MAXN];
int wei[MAXN];
int N;
inline void Down( int &x ) { x &= x - 1; }
inline void Up( int &x ) { x += x & ( - x ); }
inline void Update( int x, int v ) { for( ; x <= N ; Up( x ) ) cnt[x] += v; }
inline int Query( int x ) { int ret = 0; for( ; x ; Down( x ) ) ret += cnt[x]; return ret; }
inline void Clear() { memset( cnt, 0, ( N + 1 ) << 2 ); }
int main() {
int T; Read( T );
while( T -- ) {
Read( N );
rep( i, 1, N ) Read( perm[i] );
LL inv = 0;
Clear();
rep( i, 1, N ) {
int tmp = i - 1 - Query( perm[i] );
inv += tmp, wei[i] = i - 1 - 2 * tmp;
Update( perm[i], +1 );
}
Clear();
per( i, N, 1 ) {
int tmp = Query( perm[i] );
wei[i] += 2 * tmp;
Update( perm[i], +1 );
}
std :: sort( wei + 1, wei + 1 + N );
rep( i, 0, N ) {
Write( inv - 1ll * i * ( i - 1 ) / 2 ), putchar( " \n"[i == N] );
inv += wei[i + 1];
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
2021-07-05 [Gym101879K] Portuguese Pastimes
2020-07-05 [NOI2020省选]冰火战士