AGC023E - Inversion

Description

\(n \le 2*10^5\)
给定限制序列 \(A\)
求满足 \(P_i\le A_i\) 的所有排列中
逆序对个数的和

Solution

考虑知道一个 \(A\) 序列时怎么计算排列个数
\(C[i]\) 表示 \(A\ge i\) 的个数
然后依次决定 \(n, n-1, \cdots 1\) 填在哪里
\(k\) 时有 \(C[k]\) 个可选位置, 其中 \(n-k\) 被占用
因此总排列个数为 \(\prod_{k=1}^n C[k]-(n-k)\)
不难证明无解时该式返回0

用期望线性性去统计答案
考虑 \(i < j, P_i>P_j\)
\(A_i\le A_j\) 时, 可知 \(P_j\le A_i\), 把 \(A_j\) 改为 \(A_i\), 此时的所有排列中有一半是满足逆序对的
\(A_i\gt A_j\) 时, 转为统计 \(P_i<P_j\) 的方案数, 用总的去减这个就好了

注意到修改时把 \((A_j, A_i]\)\(C\) 都减了一, 定义这个是 \(C'\)
按权值从大到小枚举 \(A_j\)
我们要求的是 \(suff[A_i+1] * C'[A_j+1..A_i] * pref[A_j] * sign(j<i)\)
按标号维护线段树, 第一项维护在线段树里, 第二项通过打标记维护

Code

#include <bits/stdc++.h>
using namespace std;
#define ri rd<int>
#define rep(i, a, b) for (int i = (a), _ = (b); i <= _; ++i)
#define per(i, a, b) for (int i = (a), _ = (b); i >= _; --i)
#define For(i, a, b) for (int i = (a), _ = (b); i < _; ++i)
const int maxN = 2e5 + 7;
typedef long long LL;
const LL O = 1e9 + 7;

template<class T> T rd() {
    bool f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = 0;
    T x = 0; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; return f ? x : -x;
}

int n;
struct Node {
    int v, i;
    inline bool operator < (const Node &y) const {
        return v > y.v;
    }
}a[maxN];
LL c[maxN], d[maxN];
LL C[maxN], sufC[maxN];

namespace Seg {
    const int maxN = ::maxN << 1;
    struct Node;
    typedef Node* node;
    struct Node {
        node lc, rc;
        int l, mid, r;
        int sz;
        LL sum, tag;
        bool havtag;
        Node(int _sz = 0, LL _sum = 0) : sz(_sz), sum(_sum) { havtag = false; }

        Node operator + (const Node &v) const {
            return Node(sz + v.sz, (sum + v.sum) % O);
        }

        void totag(LL d) {
            (sum *= d) %= O;
            if (!havtag) havtag = true, tag = 1;
            (tag *= d) %= O;
        }

        void pushdown() {
            if (havtag) {
                lc->totag(tag);
                rc->totag(tag);
                havtag = false;
            }
        }
    }*rt, pool[maxN], *tpool = pool;

    void build(node &x, int l, int r) {
        x = tpool++;
        x->l = l, x->r = r, x->mid = (l + r) >> 1;
        if (l == r) return;
        build(x->lc, l, x->mid);
        build(x->rc, x->mid+1, r);
    }

    void init() {
        build(rt, 1, n);
    }

    void ins(node x, int to, LL d) {
        x->sz++;
        (x->sum += d) %= O;
        if (x->l == x->r) return;
        x->pushdown();
        if (to <= x->mid) ins(x->lc, to, d);
        else ins(x->rc, to, d);
    }

    void ins(int x, LL d) {
        ins(rt, x, d);
    }

    Node get(node x, int l, int r) {
        if (l <= x->l && x->r <= r) return *x;
        x->pushdown();
        if (r <= x->mid) return get(x->lc, l, r);
        if (x->mid < l) return get(x->rc, l, r);
        return get(x->lc, l, x->mid) + get(x->rc, x->mid+1, r);
    }

    Node get(int l, int r) {
        if (l > r) return Node();
        return get(rt, l, r);
    }
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("a.in", "r", stdin);
#endif

    n = ri();
    rep (i, 1, n) {
        c[a[i].v = ri()]++;
        a[i].i = i;
    }

    per (i, n, 1) c[i] += c[i+1];
    rep (i, 1, n) {
        c[i] = c[i] - (n-i);
        d[i] = c[i] - 1;
    }
    sufC[n] = 1; per (i, n-1, 0) sufC[i] = sufC[i+1] * c[i+1] % O;
    C[0] = 1; rep (i, 1, n) C[i] = C[i-1] * c[i] % O;

    Seg::init();
    sort(a+1, a+n+1);
    LL ans = 0, cnt = 0;
    for (int v = n, i = 1; v; --v) {
        LL res = 0;
        for (; i <= n && a[i].v == v; ++i) {
            Seg::Node tp = Seg::get(1, a[i].i-1);
            res -= tp.sum;
            cnt += tp.sz;
            tp = Seg::get(a[i].i+1, n);
            res += tp.sum;
            Seg::ins(a[i].i, sufC[v]);
        }
        ans += res % O * C[v] % O;
        Seg::rt->totag(d[v]);
    }
    ans = ans % O * (O+1) / 2 % O;
    ans += cnt % O * C[n] % O;
    printf("%lld\n", (ans % O + O) % O);

    return 0;
}

猜你喜欢

转载自www.cnblogs.com/acha/p/9036396.html