【ICPC】The 2021 ICPC Asia Nanjing Regional Contest (XXII Open Cup, Grand Prix of Nanjing) D

Paimon Sorting

#数据结构 #模拟

题目描述

Paimon just invents a new sorting algorithm which looks much like bubble sort, with a few differences. It
accepts a 1 1 1-indexed sequence A A A of length n and sorts it. Its pseudo-code is shown below.

在这里插入图片描述

if you don’t believe this piece of algorithm can sort a sequence it will also be your task to prove it. Anyway
here comes the question:
Given an integer sequence A = a 1 , a 2 , ⋅ ⋅ ⋅ , a n A = a_1, a_2, · · · , a_n A=a1,a2,⋅⋅⋅,an of length n, for each of its prefix Ak of length k (that is, for
each 1 ≤ k ≤ n 1 ≤ k ≤ n 1kn, consider the subsequence A k = a 1 , a 2 , ⋅ ⋅ ⋅ , a k ) , A_k = a_1, a_2, · · · , a_k), Ak=a1,a2,⋅⋅⋅,ak), count the number of swaps performed if
we call S O R T ( A k ) SORT(A_k) SORT(Ak).

输入格式

There are multiple test cases. The first line of the input contains an integer T indicating the number of test cases. For each test case:
The first line contains an integer n ( 1 ≤ n ≤ 1 0 5 ) n (1 ≤ n ≤ 10^5) n(1n105) indicating the length of the sequence.
The second line contains n n n integers a 1 , a 2 , ⋅ ⋅ ⋅ , a n ( 1 ≤ a i ≤ n ) a_1, a_2, · · · , a_n (1 ≤ a_i ≤ n) a1,a2,⋅⋅⋅,an(1ain) indicating the given sequence.
It’s guaranteed that the sum of n n n of all test cases will not exceed 1 0 6 10^6 106

输出格式

For each test case output one line containing n integers s 1 , s 2 , ⋅ ⋅ ⋅ , s n s_1, s_2, · · · , s_n s1,s2,⋅⋅⋅,sn separated by a space, where s i s_i si is
the number of swaps performed if we call S O R T ( A i ) SORT(A_i) SORT(Ai).
Please, DO NOT output extra spaces at the end of each line or your solution may be considered incorrect!

样例 #1

样例输入 #1

3
5
2 3 2 1 5
3
1 2 3
1
1

样例输出 #1

0 2 3 5 7
0 2 4
0

解法

首先,暴力的做法 n 2 n^2 n2我们可以用来检验我们的程序是否正确,考虑每个前缀之间的规律,打个表可以发现:

1. 1. 1. 当新的数 a i = max ⁡ ( a 1 , a 2 . . . , a i − 1 ) a_i = \max(a_1,a_2...,a_{i-1}) ai=max(a1,a2...,ai1)时,可以发现此时的答案 r e s i = r e s i − 1 res_i =res_{i-1} resi=resi1

2. 2. 2. 当新的数 a i < max ⁡ ( a 1 , a 2 , . . . , a i − 1 ) a_i < \max (a_1,a_2,...,a_{i-1}) ai<max(a1,a2,...,ai1)时,此时答案 r e s i = r e s i − 1 + c n t res_i = res_{i-1}+cnt resi=resi1+cnt,其中 c n t cnt cnt ( 1 , 2 , . . . , i − 1 ) (1,2,...,i-1) (1,2,...,i1)中比 a i a_i ai大的数的个数的种类。

3. 3. 3. 当新的数 a i > max ⁡ ( a i , a 2 , . . . , a i − 1 ) a_i > \max(a_i,a_2,...,a_{i-1}) ai>max(ai,a2,...,ai1)时,我们序列中 ( 1 , 2 , . . . , i − 1 ) (1,2,...,i-1) (1,2,...,i1)最大值第二次出现的的位置为 p o s pos pos,如果最大值只出现一次,那么 r e s i = r e s i − 1 + 2 res_i = res_{i-1}+2 resi=resi1+2。 如果出现次数大于一次,那么 r e s i = r e s i − 1 + ( i − p o s + 2 ) res_i = res_{i-1}+(i-pos+2) resi=resi1+(ipos+2)

因此,对于第 2 2 2种情况,我们可以使用主席树或者树状数组来维护,剩下两种情况就相对好维护一点了。

时间复杂度为 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)

代码(主席树维护)

const int N = 2e6 + 10;
 
namespace Seg {
    int tree[N];
    void add(int l, int r, int v, int x, int val) {
        if (l == r) {
            tree[v] += val;
            tree[v] = min(1ll, tree[v]);
            tree[v] = max(tree[v], 0ll);
            return;
        }
        else {
            int mid = (l + r) >> 1;
            if (x <= mid) add(l, mid, v << 1, x, val);
            else add(mid + 1, r, v << 1 | 1, x, val);
            tree[v] = tree[v << 1] + tree[v << 1 | 1];
        }
    }
    int query(int v, int l, int r, int ql, int qr) {
        if (l >= ql && r <= qr) {
            return tree[v];
        }
        int sum = 0;
        int mid = (l + r) >> 1;
        if (ql <= mid)
            sum += query(v << 1, l, mid, ql, qr);
        if (qr >= mid + 1)
            sum += query(v << 1 | 1, mid + 1, r, ql, qr);
        return sum;
    }
 
};
 
void solve() {
    int n;
    std::cin >> n;
 
    std::vector<int>a(n + 1);
    for (int i = 1; i <= n; ++i) {
        std::cin >> a[i];
    }
 
 
    std::vector<int>res;
 
    int mx, cnt=0, tmp = 0,last = 0;
    for (int i = 1;i<=n; ++i) {
        if (i == 1) {
            res.push_back(last);
            mx = a[i], cnt = 1;
        }
        else {
            if (a[i] == mx) {
                res.push_back(last);
                cnt++;
                if (cnt >= 2) tmp++;
            }
            else if (a[i] < mx) {
                last = last + Seg:: query(1, 1, n, a[i] + 1, n);
                res.push_back(last);
                if (cnt >= 2) tmp++;
            }
            else {
                last = last + 2 + tmp;
                res.push_back(last);
                mx = a[i];
                tmp = 0;
                cnt = 1;
            }
        }
        Seg::add(1, n, 1, a[i], 1);
    }
 
    for (int i = 1; i <= n; i++) {
        Seg::add(1, n, 1, a[i], -1);
        if (i == n) {
            cout << res[i - 1];
            break;
        }
        cout << res[i - 1] << " ";
    }
    cout << '\n';
 
}
 
signed main() {
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);
 
    int t = 1;
    std::cin >> t;
 
    while (t--) {
        solve();
    }
 
    return 0;
}

代码(树状数组维护)


const int N = 1e5 + 10;
 
int n;
namespace BIT {
    int tree[N];
    void init() {
        for (int i = 1; i <= n; ++i) {
            tree[i] = 0;
        }
    }
 
    inline int lowbit(int x) {
        return x & (-x);
    }
 
    void add(int x, int val) {
        while (x <= n) {
            tree[x] += val;
            x += lowbit(x);
        }
    }
 
    int query(int x) {
        int res = 0;
        while (x > 0) {
            res += tree[x];
            x -= lowbit(x);
        }
 
        return res;
    }
};
 
void solve() {
    std::cin >> n;
 
 
    std::vector<int>a(n + 1);
    for (int i = 1; i <= n; ++i) {
        std::cin >> a[i];
    }
 
    std::vector<int>res(n + 1);
    res[1] = 0;
 
    std::vector<bool>vis(n + 1);
    vis[a[1]] = 1;
    BIT::add(a[1], 1);
 
    int maxx = a[1], cnt = 1, pos = -1;
    for (int i = 2; i <= n; ++i) {
        if (!vis[a[i]]) {
            vis[a[i]] = 1;
            BIT::add(a[i], 1);
        }
 
        if (a[i] == maxx) {
            res[i] = res[i - 1];
            cnt++;
            if (cnt == 2) pos = i;
        }
        else if (a[i] < maxx) {
            res[i] = res[i - 1] + BIT::query(maxx) - BIT::query(a[i]);
        }
        else {
            if (pos == -1) res[i] = res[i - 1] + 2;
            else {
                res[i] = res[i - 1] + i - pos + 2;
            }
            maxx = a[i];
            pos = -1;
            cnt = 1;
        }
    }
 
    for (int i = 1; i <= n; ++i) {
        if (vis[a[i]]) {
            vis[a[i]] = 0;
            BIT::add(a[i], -1);
        }
        std::cout << res[i] << " \n"[i == n];
 
    }
 
    BIT::init();
 
}
 
signed main() {
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);
 
    int t = 1;
    std::cin >> t;
 
    while (t--) {
        solve();
    }
 
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值