谈谈逆序对

谈谈逆序对

归并排序

归并排序将一个大区间分成两个小区间,其中保证了右区间的\(id\)是比左区间大的,且两区间分别有序。

所以我们只需要逐个对照,判断有多少对\(a[j] < a[i]\)\(i\)为左指针,\(j\)为右指针),遇到\(a[j] < a[i]\)的情况就说明\(a[j]\)与所有未被合并的左区间点都能产生逆序对,提供\(mid - i + 1\)的贡献。因为此时的\(a[i]\)是左区间所有未被合并的点的最小值。即\(id[j] > id[i]\) && \(a[j] < a[i]\)

#ifndef DEBUG
    #define NDEBUG
#endif

#include <bits/stdc++.h>

using namespace std;

namespace IO {
    char ibuf[1 << 21], *p1 = ibuf, *p2 = ibuf, obuf[1 << 21], *p = obuf;

#ifdef DEBUG
    inline char gc() {
        return getchar();
    }

    inline void pc(char ch) {
        putchar(ch);
    }

    struct outflush {};

#else
    inline char gc() {
        return p1 == p2 && (p2 = (p1 = ibuf) + fread(ibuf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++;
    }

    inline void pc(char ch) {
        p == obuf + sizeof(obuf) && (fwrite(obuf, 1, 1 << 21, stdout), p == obuf), *p++ = ch;
    }

    inline void ioflush() {
        fwrite(obuf, 1, p - obuf, stdout), fflush(stdout), p = obuf;
    }

    struct outflush {
        ~outflush() {
            ioflush();
            fclose(stdin);
            fclose(stdout);
        }
    } flushout;

#endif
    inline void read() {}
    template<class T, class... Args> inline void read(T &x, Args&... args) {
        x = 0; char ch; bool sgn = true;
        while (!isdigit(ch = gc())) sgn = ch != '-';
        if (sgn) do x = x * 10 + (ch & 15); while (isdigit(ch = gc()));
        else     do x = x * 10 - (ch & 15); while (isdigit(ch = gc()));
        read(args...);
    }

    template<class T> inline void prin(T x, char ch = EOF) {
        if (x < 0) pc('-'), x = ~x + 1;
        static int buf[sizeof(T) * 3], top = 0;
        while (x) buf[++top] = x % 10, x /= 10;
        if (!top) buf[++top] = 0;
        while (top) pc(buf[top--] ^ 48);
        if (~ch) pc(ch);
    }
}

using IO::gc, IO::pc, IO::read, IO::prin, IO::outflush;

using ll = long long;

const int MN = 5e5 + 5;

int n;
ll ans;
int a[MN], tmp[MN];

void merge_sort(int l, int r) {
    if (l == r) return ;
    int mid = l + (r - l >> 1), i = l, j = mid + 1, k = l;
    merge_sort(l, mid), merge_sort(mid + 1, r);
    while (i <= mid && j <= r) {
        if (a[i] <= a[j]) tmp[k++] = a[i++];
        else {
            tmp[k++] = a[j++];
            ans += mid - i + 1;
        }
    }
    while (i <= mid) tmp[k++] = a[i++];
    while (j <= r) tmp[k++] = a[j++];
    for (int p = l; p <= r; ++p) a[p] = tmp[p];
}

signed main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    read(n);
    for (int i = 1; i <= n; ++i) read(a[i]);
    merge_sort(1, n);
    prin(ans, '\n');
    return 0;
}

树状数组

一种不离散化,最简单思路:按照\(val\)自小到大排序,然后挨个把它们的\(id\)加进树状数组,每次的贡献为\(i - query(id[i])\)(即总共\(i\)个数,抛去比\(id\)\(i\)小的数的个数,我们排序已经保证其他数的\(val\)\(i\)小,只需要找\(id\)\(i\)大的数即可。

#ifndef DEBUG
    #define NDEBUG
#endif

#include <bits/stdc++.h>

using namespace std;

namespace IO {
    char ibuf[1 << 21], *p1 = ibuf, *p2 = ibuf, obuf[1 << 21], *p = obuf;

#ifdef DEBUG
    inline char gc() {
        return getchar();
    }

    inline void pc(char ch) {
        putchar(ch);
    }

    struct outflush {};

#else
    inline char gc() {
        return p1 == p2 && (p2 = (p1 = ibuf) + fread(ibuf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++;
    }

    inline void pc(char ch) {
        p == obuf + sizeof(obuf) && (fwrite(obuf, 1, 1 << 21, stdout), p == obuf), *p++ = ch;
    }

    inline void ioflush() {
        fwrite(obuf, 1, p - obuf, stdout), fflush(stdout), p = obuf;
    }

    struct outflush {
        ~outflush() {
            ioflush();
            fclose(stdin);
            fclose(stdout);
        }
    } flushout;

#endif
    inline void read() {}
    template<class T, class... Args> inline void read(T &x, Args&... args) {
        x = 0; char ch; bool sgn = true;
        while (!isdigit(ch = gc())) sgn = ch != '-';
        if (sgn) do x = x * 10 + (ch & 15); while (isdigit(ch = gc()));
        else     do x = x * 10 - (ch & 15); while (isdigit(ch = gc()));
        read(args...);
    }

    template<class T> inline void prin(T x, char ch = EOF) {
        if (x < 0) pc('-'), x = ~x + 1;
        static int buf[sizeof(T) * 3], top = 0;
        while (x) buf[++top] = x % 10, x /= 10;
        if (!top) buf[++top] = 0;
        while (top) pc(buf[top--] ^ 48);
        if (~ch) pc(ch);
    }
}

using IO::gc, IO::pc, IO::read, IO::prin, IO::outflush;

using ll = long long;

const int MN = 5e5 + 5;

int n;
ll ans;
int sum[MN];
pair<int, int> a[MN];

void add(int x, int y) {
    for (; x <= n; x += x & -x) sum[x] += y;
}

ll query(int x) {
    ll ret = 0;
    for (; x; x -= x & -x) ret += sum[x];
    return ret;
}

signed main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    read(n);
    for (int i = 1; i <= n; ++i) {
        read(a[i].second);
        a[i].first = i;
    }
    sort(a + 1, a + n + 1, [](pair<int, int> x, pair<int, int> y) {
        return x.second != y.second ? x.second < y.second : x.first < y.first;
    });
    for (int i = 1; i <= n; ++i) {
        add(a[i].first, 1);
        ans += i - query(a[i].first);
    }
    prin(ans, '\n');
    return 0;
}

第二种,设一数组\(rnk\),将\(rnk[id[i]]\)记为\(i\),即下标为它的\(id\)\(val\)为它的实际数值的排名。

\(for\)循环从1到n遍历\(rnk[i]\),保证\(id\)一定从小到大,每次把\(rnk[i]\)加入树状数组,相当于是把它的排名加入树状数组,每次查询有多少比它排名大的数,即\(ans += i - query(rnk[i])\)

#ifndef DEBUG
    #define NDEBUG
#endif

#include <bits/stdc++.h>

using namespace std;

namespace IO {
    char ibuf[1 << 21], *p1 = ibuf, *p2 = ibuf, obuf[1 << 21], *p = obuf;

#ifdef DEBUG
    inline char gc() {
        return getchar();
    }

    inline void pc(char ch) {
        putchar(ch);
    }

    struct outflush {};

#else
    inline char gc() {
        return p1 == p2 && (p2 = (p1 = ibuf) + fread(ibuf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++;
    }

    inline void pc(char ch) {
        p == obuf + sizeof(obuf) && (fwrite(obuf, 1, 1 << 21, stdout), p == obuf), *p++ = ch;
    }

    inline void ioflush() {
        fwrite(obuf, 1, p - obuf, stdout), fflush(stdout), p = obuf;
    }

    struct outflush {
        ~outflush() {
            ioflush();
            fclose(stdin);
            fclose(stdout);
        }
    } flushout;

#endif
    inline void read() {}
    template<class T, class... Args> inline void read(T &x, Args&... args) {
        x = 0; char ch; bool sgn = true;
        while (!isdigit(ch = gc())) sgn = ch != '-';
        if (sgn) do x = x * 10 + (ch & 15); while (isdigit(ch = gc()));
        else     do x = x * 10 - (ch & 15); while (isdigit(ch = gc()));
        read(args...);
    }

    template<class T> inline void prin(T x, char ch = EOF) {
        if (x < 0) pc('-'), x = ~x + 1;
        static int buf[sizeof(T) * 3], top = 0;
        while (x) buf[++top] = x % 10, x /= 10;
        if (!top) buf[++top] = 0;
        while (top) pc(buf[top--] ^ 48);
        if (~ch) pc(ch);
    }
}

using IO::gc; using IO::pc; using IO::read; using IO::prin; using IO::outflush;

using ll = long long;

const int MN = 5e5 + 5;

int n;
ll ans;
int sum[MN];
ll rnk[MN];
pair<int, int> a[MN];

void add(int x, int y) {
    for (; x <= n; x += x & -x) sum[x] += y;
}

ll query(int x) {
    ll ret = 0;
    for (; x; x -= x & -x) ret += sum[x];
    return ret;
}

signed main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    read(n);
    for (int i = 1; i <= n; ++i) {
        read(a[i].first);
        a[i].second = i;
    }
    sort(a + 1, a + n + 1, [](const pair<int, int> &x, const pair<int, int> &y) {
        return x.first != y.first ? x.first < y.first : x.second < y.second;
    });
    for (int i = 1; i <= n; ++i) rnk[a[i].second] = i;
    for (int i = 1; i <= n; ++i) {
        add(rnk[i], 1);
        ans += i - query(rnk[i]);
    }
    prin(ans, '\n');
    return 0;
}
posted @ 2022-07-06 22:30  zjsqwq  阅读(33)  评论(0)    收藏  举报