AGC023E - Inversion
Description
n≤2∗105n≤2∗105
给定限制序列 AA
求满足 Pi≤AiPi≤Ai 的所有排列中
逆序对个数的和
Solution
考虑知道一个 AA 序列时怎么计算排列个数
记 C[i]C[i] 表示 A≥iA≥i 的个数
然后依次决定 n,n−1,⋯1n,n−1,⋯1 填在哪里
填 kk 时有 C[k]C[k] 个可选位置, 其中 n−kn−k 被占用
因此总排列个数为 ∏nk=1C[k]−(n−k)∏nk=1C[k]−(n−k)
不难证明无解时该式返回0
用期望线性性去统计答案
考虑 i<j,Pi>Pji<j,Pi>Pj
当 Ai≤AjAi≤Aj 时, 可知 Pj≤AiPj≤Ai, 把 AjAj 改为 AiAi, 此时的所有排列中有一半是满足逆序对的
当 Ai>AjAi>Aj 时, 转为统计 Pi<PjPi<Pj 的方案数, 用总的去减这个就好了
注意到修改时把 (Aj,Ai](Aj,Ai] 的 CC 都减了一, 定义这个是 C′
按权值从大到小枚举 Aj
我们要求的是 suff[Ai+1]∗C′[Aj+1..Ai]∗pref[Aj]∗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;
}
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 继承的思维:从思维模式到架构设计的深度解析
· 如何在 .NET 中 使用 ANTLR4
· 后端思维之高并发处理方案
· 理解Rust引用及其生命周期标识(下)
· 从二进制到误差:逐行拆解C语言浮点运算中的4008175468544之谜
· 35岁程序员的中年求职记:四次碰壁后的深度反思
· 当职场成战场:降职、阴谋与一场硬碰硬的抗争
· 用99元买的服务器搭一套CI/CD系统
· Excel百万数据如何快速导入?
· ShadowSql之.net sql拼写神器