[USACO2019JAN]Sleepy Cow Sorting题解

拿到这个问题,我们从头开始思考。
我们把序列看做两部分,一部分在前表示待排序的,记为序列1,一部分在后表示已排序的,记为序列2
因为序列2在后,所以不必担心它影响序列1的排序,那么对于序列1的第一个元素,显然珂以放到序列2的某个对应位置,使序列2仍然保持有序
那么很简单,我们发现只需要将序列1每个元素都移动一次即可完成排序,这显然是最优的
讲一下初始化,对于序列2,我们发现显然从最后一个逆序对的第二个元素开始一直到序列的最后一个元素珂以直接作为序列2的,那么剩下的元素按原来的顺序放入序列1,接下来我们珂以直接模拟这个操作。
目前的复杂度是\(\Theta(n^2)\),显然对于本题来说无法通过。
那么消耗时间比较多的部分是什么呢?就是如何找到序列1的第一个元素在序列2中的对应位置(换而言之就是计算答案)。
我们需要在\(\Theta(log_2n)\)的时间内求出序列1的第一个元素在序列2中的对应位置(为什么是\(\Theta(log_2n)\)而不是\(\Theta(1)\)看数据范围就知道啦)。首先我们发现序列2是有序的,无需模拟,然后怎么做呢?基于\(\Theta(log_2n)\)的复杂度,我们想到了神奇的树状数组,开始在序列2中的所有元素的位置上插入一个1,然后对于序列1的第i个元素,记为\(a_i\),我们只需要查询\([1,a_i)\)有几个数就知道答案了,然后再往\(a_i\)的位置上插入一个1,持续模拟即可。

好了放个代码

#include <cstdio>
#include <vector>
#define ll long long

using namespace std;

ll read(){
    ll x = 0; int zf = 1; char ch = ' ';
    while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
    if (ch == '-') zf = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x * zf;
}

int p[100005];
int fen[100005];

#define lowbit(x) (x&(-x))

int n;

void add(int pos){
    for ( ; pos <= n; pos += lowbit(pos)) ++fen[pos];
}

int query(int pos){
    int sum = 0;
    for ( ; pos; pos -= lowbit(pos)) sum += fen[pos];
    return sum;
}

vector<int> ans(0); 

int main(){
    n = read(); bool flg = 1;
    for (int i = 1; i <= n; ++i){
        p[i] = read();
        if (p[i] != i) flg = 0;
    }
    if (flg){puts("0"); return 0;}
    int i;
    for (i = n; i >= 1; --i)
        if (p[i] < p[i - 1])
            break;
    for (int j = i; j <= n; ++j)
        add(p[j]);
    for (int j = 1; j < i; ++j){
        ans.push_back(query(p[j]) + i - j - 1);
        add(p[j]);
    }
    printf("%d\n", ans.size());
    for (int j = 0; j < ans.size(); ++j)
        printf("%d%c", ans[j], ((j == ans.size() - 1) ? '\n' : ' '));
    return 0;
}
posted @ 2019-06-17 13:08  LinZhengmin  阅读(652)  评论(0编辑  收藏  举报

Contact with me