D. Yet Another Inversions Problem

D. Yet Another Inversions Problem

You are given a permutation p0,p1,,pn1 of odd integers from 1 to 2n1 and a permutation q0,q1,,qk1 of integers from 0 to k1.

An array a0,a1,,ank1 of length nk is defined as follows:

aik+j=pi2qj for all 0i<n and all 0j<k

For example, if p=[3,5,1] and q=[0,1], then a=[3,6,5,10,1,2].

Note that all arrays in the statement are zero-indexed. Note that each element of the array a is uniquely determined.

Find the number of inversions in the array a. Since this number can be very large, you should find only its remainder modulo 998244353.

An inversion in array a is a pair (i,j) (0i<j<nk) such that ai>aj.

Input

The first line contains a single integer t (1t104) — the number of test cases.

The first line of each test case contains two integers n and k (1n,k2105) — the lengths of arrays p and q.

The second line of each test case contains n distinct integers p0,p1,,pn1 (1pi2n1, pi is odd) — the array p.

The third line of each test case contains k distinct integers q0,q1,,qk1 (0qi<k) — the array q.

It is guaranteed that the sum of n over all test cases doesn't exceed 2105 and the sum of k over all test cases doesn't exceed 2105.

Output

For each test case, output one integer: the number of inversions in array a modulo 998244353.

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 a is equal to [3,6,5,10,1,2]. There are 9 inversions in it: (0,4), (0,5), (1,2), (1,4), (1,5), (2,4), (2,5), (3,4), (3,5). Note that these are pairs (i,j) such that i<j and ai>aj.

In the second test case, array a is equal to [8,4,1,2,24,12,3,6,40,20,5,10]. There are 25 inversions in it.

In the third test case, array a is equal to [1,2,4,8,16]. There are no inversions in it.

 

解题思路

  题解给出的做法挺麻烦的,这里提供另外一种思路。

  把题目描述的长度为 nk 的序列 a 看作是一个 n×k 的矩阵 A,则有 Ai,j=pi2qj。另外 ai 对应于 Ax,y,其中 (x,y) 等于 (in,imodk)

  求序列的逆序对个数的做法是枚举每一个元素 ai 并统计 aj,j[0,i1] 中比 ai 严格大的元素数量。对应到矩阵中就是枚举每一个元素 Ax,y(下图红色部分),分别统计 Ax,j,j[0,y1](下图橙色部分)和 Ai,j,{(i,j)0ix1,0j<k}(下图绿色部分)中比 Ax,y 严格大的元素数量。

  首先橙色部分很容易统计,只需求序列 q 的某个前缀的逆序对数量即可。定义 f(0,i) 表示 q0qi 的逆序对的数量,因此所有 Ax,y 的橙色部分的逆序对总数就是 n×i=0k1f(0,i),可以用树状数组求逆序对的做法以 O(klogk) 的时间复杂度求出来。

  考虑绿色的部分。首先注意到每个值都是 pi2qj 的形式,而 pi 最大只有 41051,意味着对于另外一个值 pi2qj,如果 qjqj>18 则必然有 pi2qj>pi2qj,因为 219>4105。所以对于 Ax,y,我们只需考虑枚举绿色部分中 qj 在范围 [qy18,qy+18] 的列中的元素,统计比 Ax,y 大的元素数量即可。对于 qj>qy+18 的部分,这些列的元素必定比 Ax,y 大,总的数量是 (m1(qy+18))×x。对于 qj<qy18 的部分,这些列的元素必定比 Ax,y 小,不用统计。

  因此很朴素的思想是先考虑某一行 x,然后依次枚举 y,找到 qj[qy18,qy+18] 中的列,对于每一列求序列 p 的前缀 p0px1 中比 px2qyqj 大的元素数量(树状数组实现)。再考虑回 n 行,总的时间复杂度就是 O(nklog2n)

  实际上并不用枚举 y,因为可以发现不管 y 的值是多少,pypj 总是在区间 [18,18] 内,因此 px2qyqj 可能的值不超过 37 个。所以只用考虑在 1k1 中有多少个值能使得 px2qyqj 取到 px218,px217,,px218

  首先很明显 1k1 中每个值都能得到 px20。再考虑 px21px218,对于某个 px2i,很明显只有 v[i,k1] 中的数能存在某个 j[0,k1] 使得 vj=i。同理对于 px218px21 中的某个 px2i,只有 v[0,k1i] 中的数能得到。

  另外还要考虑 1k1 所有值的大于 px218 的部分。对于某个值 iv[i+19,k1] 的值都满足 vi>18,因此这些元素的总和就是x×i=0k1max{0,ki19}=x×i=0k19ki19

  定义 g(i,j) 表示 p0pi 中严格大于 j 的元素数量,因此某一行 x 的所有的 y 在绿色部分中的逆序对数量就是x×i=0k19mi19+mg(x1,px)+i=118(ki)(g(x1,px2i)+g(x1,px2i))

  AC 代码如下,时间复杂度为 O(nlog2n)

#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

posted @   onlyblues  阅读(60)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示