zoukankan      html  css  js  c++  java
  • 题解-P3149 【排序】

    (Large exttt{P3149})

    标签:树状数组(求逆序对)

    有点套路

    题意

    在一段序列中,每次选定一个数,将这个序列中小于这个数的所有数排序,并顺序插入它们原来的位置上。(本次操作会对下一次影响

    问每次操作完了之后(和没操作时)的逆序对个数。

    这道题目的逆序对意思有点奇怪,刚开始的逆序对包括相等的两个数

    思路

    以下操作 (n) 指对第 (n) 个数进行上述操作

    先离散化。

    对于一个较大数的操作,是可以从较小的数的操作推导过来的,我们可以这样想:

    假设现在已经对于数操作 (n) 完了,那么对于操作 (n+1) ,它相对于上一个操作减少的逆序对只是在每个数 (n+1) 后面小于这个数的个数和。

    可以这样理解:

    1. 第一种逆序对,是两个小于 (n+1) 的数组成,显然已经没有了,因为操作n已经去除了。

    2. 第二种逆序对,是一个数 (n+1) 和小于 (n+1) 的数组成,这种会减少(排序一下,小的数到它前面了),也是要计算的。

    3. 第三种逆序对,是一个数 (n+1) 和大于 (n+1) 的数组成,这可能会改变,但是操作过后每个数 (n+1) 的位置上会变为小于等于自己的数,和大于 (n+1) 组成的逆序对个数一样,因此,并未改变。

    4. 第四种逆序对,是两个大于 (n+1) 的数组成,显然个数不变。

    理清了思路,发现第二种逆序对正好可以用树状数组动态解决,操作 (n) 过后,维护小于等于 (n) 个数的前缀。

    并且注意,它的操作是会延续到下一次的,因此注意下下一次操作小于本次操作。

    时间复杂度 ( exttt{O(N logN)})

    注意会暴 ( exttt{int})

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    #define PB push_back
    #define int long long
    const int N = 1e6;
    inline int read()
    {
        int s = 0;
        register bool neg = 0;
        register char c = getchar();
        for (; c < '0' || c > '9'; c = getchar())
            neg |= (c == '-');
        for (; c >= '0' && c <= '9'; s = s * 10 + (c ^ 48), c = getchar())
            ;
        s = (neg ? -s : s);
        return s;
    }
    
    int a, b, s[N + 10], p[N + 10], top, Ans[N + 10], q[N + 10], t[N + 10];
    vector<int> id[N + 10];
    
    inline void add(int n, int m)
    {
        for (; n <= a; n += n & -n)
            q[n] += m;
    }
    
    inline int query(int n)
    {
        int ans = 0;
        for (; n; n -= n & -n)
            ans += q[n];
        return ans;
    }
    
    signed main()
    {
        a = read();
        b = read();
        for (int i = 1; i <= a; i++)
            s[i] = p[i] = read();
        sort(p + 1, p + a + 1);
        top = unique(p + 1, p + a + 1) - p - 1;
        for (int i = 1; i <= a; i++)
            s[i] = lower_bound(p + 1, p + top + 1, s[i]) - p;
        int ans = 0, qq = 0;
        for (int i = 1; i <= a; i++)
        {
            add(s[i], 1);//求一开始的逆序对。
            qq += t[s[i]];
            ans += i - query(s[i]);
            t[s[i]]++;
        }
        memset(q, 0, sizeof(q));
        ans += qq;
        printf("%lld
    ", ans);
        for (int i = 1; i <= a; i++)
            id[s[i]].PB(i);
        int sum = 0;
        for (int i = 1; i <= top; i++)
        {
            for (int j = 0; j < id[i].size(); j++)
            {
                ans -= sum - query(id[i][j]);
            }
            ans -= id[i].size() - 1;//应对题目奇奇怪怪的逆序对定义。
            for (int j = 0; j < id[i].size(); j++)
            {   
                add(id[i][j], 1);//维护小于等于n个数的前缀
                Ans[id[i][j]] = ans;//储存答案
            }
            sum += id[i].size();
        }
        int x;
        int mx = 1e18;
        for (int i = 1; i <= b; i++)
        {
            x = read();
            mx = min(mx, Ans[x]);
            printf("%lld
    ", mx);
        }
        return 0;
    }
    
  • 相关阅读:
    windows安装kafka
    excel打开utf-8的csv乱码
    laravel 记录慢sql日志
    php ftp连接的坑
    公用辅助方法
    ubuntu重置网络配置
    php socket
    docker ftp配置多个用户
    php aes-ecb-128位加密
    redis集群 哨兵模式
  • 原文地址:https://www.cnblogs.com/RedreamMer/p/13778464.html
Copyright © 2011-2022 走看看