D. Yet Another Inversions Problem
D. Yet Another Inversions Problem
You are given a permutation of odd integers from to and a permutation of integers from to .
An array of length is defined as follows:
For example, if and , then .
Note that all arrays in the statement are zero-indexed. Note that each element of the array is uniquely determined.
Find the number of inversions in the array . Since this number can be very large, you should find only its remainder modulo .
An inversion in array is a pair () such that .
Input
The first line contains a single integer () — the number of test cases.
The first line of each test case contains two integers and () — the lengths of arrays and .
The second line of each test case contains distinct integers (, is odd) — the array .
The third line of each test case contains distinct integers () — the array .
It is guaranteed that the sum of over all test cases doesn't exceed and the sum of over all test cases doesn't exceed .
Output
For each test case, output one integer: the number of inversions in array modulo .
Example
Input
4
3 2
3 5 1
0 1
3 4
1 3 5
3 2 0 1
1 5
1
0 1 2 3 4
8 3
5 1 7 11 15 3 9 13
2 0 1
Output
9
25
0
104
Note
In the first test case, array is equal to . There are inversions in it: , , , , , , , , . Note that these are pairs such that and .
In the second test case, array is equal to . There are inversions in it.
In the third test case, array is equal to . There are no inversions in it.
解题思路
题解给出的做法挺麻烦的,这里提供另外一种思路。
把题目描述的长度为 的序列 看作是一个 的矩阵 ,则有 。另外 对应于 ,其中 等于 。
求序列的逆序对个数的做法是枚举每一个元素 并统计 中比 严格大的元素数量。对应到矩阵中就是枚举每一个元素 (下图红色部分),分别统计 (下图橙色部分)和 (下图绿色部分)中比 严格大的元素数量。
首先橙色部分很容易统计,只需求序列 的某个前缀的逆序对数量即可。定义 表示 的逆序对的数量,因此所有 的橙色部分的逆序对总数就是 ,可以用树状数组求逆序对的做法以 的时间复杂度求出来。
考虑绿色的部分。首先注意到每个值都是 的形式,而 最大只有 ,意味着对于另外一个值 ,如果 则必然有 ,因为 。所以对于 ,我们只需考虑枚举绿色部分中 在范围 的列中的元素,统计比 大的元素数量即可。对于 的部分,这些列的元素必定比 大,总的数量是 。对于 的部分,这些列的元素必定比 小,不用统计。
因此很朴素的思想是先考虑某一行 ,然后依次枚举 ,找到 在 中的列,对于每一列求序列 的前缀 中比 大的元素数量(树状数组实现)。再考虑回 行,总的时间复杂度就是 。
实际上并不用枚举 ,因为可以发现不管 的值是多少, 总是在区间 内,因此 可能的值不超过 个。所以只用考虑在 中有多少个值能使得 取到 。
首先很明显 中每个值都能得到 。再考虑 ,对于某个 ,很明显只有 中的数能存在某个 使得 。同理对于 中的某个 ,只有 中的数能得到。
另外还要考虑 所有值的大于 的部分。对于某个值 , 的值都满足 ,因此这些元素的总和就是
定义 表示 中严格大于 的元素数量,因此某一行 的所有的 在绿色部分中的逆序对数量就是
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10, mod = 998244353;
int n, m, k;
int p[N], q[N];
int tr[N * 2];
int lowbit(int x) {
return x & -x;
}
void add(int x) {
for (int i = x; i <= k; i += lowbit(i)) {
tr[i]++;
}
}
int query(int x) {
int ret = 0;
for (int i = x; i; i -= lowbit(i)) {
ret += tr[i];
}
return ret;
}
void solve() {
scanf("%d %d", &n, &m);
for (int i = 0; i < n; i++) {
scanf("%d", p + i);
}
for (int i = 0; i < m; i++) {
scanf("%d", q + i);
}
k = max(2 * n - 1, m);
int ret = 0;
memset(tr, 0, k + 10 << 2);
for (int i = 0; i < m; i++) {
ret = (ret + i - query(q[i] + 1)) % mod;
add(q[i] + 1);
}
ret = 1ll * ret * n % mod;
memset(tr, 0, k + 10 << 2);
for (int i = 0; i < n; i++) {
if (m > 18) ret = (ret + (m - 18ll) * (m - 19) / 2 % mod * i) % mod;
ret = (ret + 1ll * (i - query(p[i])) * m) % mod;
for (int j = 1; j <= 18 && j < m; j++) {
ret = (ret + 1ll * (i - query(min((LL)k, (LL)p[i] << j))) * (m - j)) % mod;
ret = (ret + 1ll * (i - query(p[i] >> j)) * (m - j)) % mod;
}
add(p[i]);
}
printf("%d\n", ret);
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
参考资料
Editorial of Codeforces Round 917 (Div. 2):https://codeforces.com/blog/entry/123721
Codeforces Round 917 (Div. 2) A - F:https://zhuanlan.zhihu.com/p/673963234
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17928855.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效