251112D. 谜题(poem)
给定长为 \(n\) 的排列 \(a\)。有 \(k\) 次交换,每次在所有的 \(\binom n2\) 对数中随机一对交换。问最终逆序对数的期望。
但是这样还不够!有 \(q\) 次修改,将第 \(i\) 次交换改为交换给出的 \((x_i,y_i)\)。问最终逆序对数的期望。
先不管修改。然后乘上 \({\binom n2}^k\) 变成计数。
计算随机操作后逆序对个数的一个套路是考虑每对数在最后被交换的概率。
考虑一对数 \((A,B)\)。将剩下的其他数看作 \(C\)。那么最终状态下原来的位置上的数有 \(7\) 种可能:\(AB,BA,AC,CA,AB,BA,CC\)
一次交换对状态的影响可以简单在状态间转移。
细节
假如当前是 \(AB\),
- 保持 \(AB\),有 \(\binom{n-2}2\) 种方案。
- 变成 \(AC\),\(n-2\) 种方案。
- 变成 \(CB\),\(n-2\) 种方案。
- 变成 \(BA\),\(1\) 种方案。
\(BA\) 类似。
假如当前是 \(AC\),
- 保持 \(AC\),有 \(\binom{n-2}2+n-3\) 种方案。
- 变成 \(CC\),\(n-3\) 种方案。
- 变成 \(BC\),\(1\) 种方案。
- 变成 \(AB\),\(1\) 种方案。
- 变成 \(CA\),\(1\) 种方案。
\(CA\),\(BC\),\(CB\) 类似。
假如当前是 \(CC\),
- 保持 \(CC\),有 \(\binom{n-2}2+2(n-4)+1\) 种方案。
- 变成 \(AC\),\(BC\),\(CA\) 或者 \(CB\),\(1\) 种方案。
发现转移是线性的,且对于任意位置对是相同的。
矩阵快速幂加速即可快速算出 \(k\) 次操作后的方案数。
枚举每两对位置,计算其所有状态对应的期望逆序对数量。
另外一些细节
考虑每一种 \(C\) 取值共 \(n-2\) 种方案。由于所有 \(C\) 不区分,所以最终 \(C\) 是每个值的概率都是 \(\frac 1{n-2}\)。
接下来考虑每个位置对 \((x,y)\) 假如 \(x<y\) 且 \(a_x<a_y\)(顺序对),那么:
- \(AB\) 贡献 \(0\)。
- \(BA\) 贡献 \(n-2\)。
- \(AC\) 贡献 \(a_x-1\)。
- \(BC\) 贡献 \(a_y-2\)。
- \(CA\) 贡献 \(n-a_x-1\)。
- \(BC\) 贡献 \(n-a_y\)。
- \(CC\) 贡献 \(\frac 12\)。
逆序对类似。
由于贡献全部都是线性的,可以从小到大枚举 \(y\),维护顺/逆序对的个数以及 \(x\) 的和。
用树状数组可以在 \(O(n\log n)\) 时间内解决。
考虑带上修改。我们可以每次删去原有的 \(x,y\) 的贡献,然后加回新的 \(x,y\) 的贡献。
为了计算贡献,需要维护对于每个位置,其 之前/之后 小于/大于 其值的位置的 个数/和。
看起来像是一个在线带修二维偏序。但是实际上我们可以离线转化为三位偏序用 cdq 处理。
我们将一个修改改为为加入一个同一时刻的原位置原权值的系数为 \(-1\) 的点,以抵消原来的贡献,然后加入新点。
那么在递归时计算左侧所有点对右侧所有点的贡献。左右侧分别按下标排序,然后做双指针保证偏序,用树状数组维护数量/和。为了考虑到全部共 \(4\) 种情况,需要对 \(p_左<p_右\) / \(p_左>p_右\) 的情况各做一遍,树状数组同时维护 前/后 缀的情况。
最后还有一点小问题就是钦定完一次操作后,相当于 \(k\) 的值减少了 \(1\)。这时各状态的方案数会变化。但是可以观察到贡献关于状态的方案数也是线性的,那么只维护系数即可。
#include <functional>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <array>const int N = 1e5 + 7;const int MOD = 1e9 + 7;
auto gmod = [](auto&& x) { return x - (x >= MOD) * MOD; };
struct mi { int w; mi(){} mi(int _w): w(_w) {} inline int load() { return w; } inline bool empty() { return !w; } };
inline mi operator + (const mi& x, const mi& y) { return gmod(x.w + y.w); }
inline mi operator - (const mi& x, const mi& y) { return gmod(x.w + MOD - y.w); }
inline mi operator - (const mi& x) { return x.w ? MOD - x.w : 0; }
inline mi operator * (const mi& x, const mi& y) { return 1ull * x.w * y.w %MOD; }
inline void operator += (mi& x, const mi& y) { x.w += y.w, x.w -= (x.w >= MOD) * MOD; }
inline void operator -= (mi& x, const mi& y) { x.w += MOD - y.w, x.w -= (x.w >= MOD) * MOD; }
inline void operator *= (mi& x, const mi& y) { x.w = 1ull * x.w * y.w %MOD; }
inline mi fpow(mi x, int k) { mi r = 1; for(; k; k >>= 1, x *= x) if(k & 1) r *= x; return r; }typedef long long i64;
enum Index { AB=0, AC, BA, BC, CA, CB, CC };struct matrix {mi matr[7][7];inline void reset() { memset(matr, 0, sizeof matr); }inline mi* operator[] (int k) { return matr[k]; }inline const mi* operator[] (int k) const { return matr[k]; }
};inline matrix operator* (const matrix& a, const matrix& b) {matrix matr; matr.reset();for(int i = 0; i < 7; ++i) {for(int k = 0; k < 7; ++k) {for(int j = 0; j < 7; ++j) {matr[i][j] += a[i][k] * b[k][j];}}}return matr;
}mi C(int n) {return (1ll * n * (n - 1) / 2) %MOD;
}matrix build(int n) {matrix r; r.reset();const mi Cn = C(n-2);r[AB][BA] = 1;r[AB][AB] = Cn;r[AB][AC] = r[AB][CB] = n - 2;r[BA][AB] = 1;r[BA][BA] = Cn;r[BA][CA] = r[BA][BC] = n - 2;r[AC][AB] = r[AC][BC] = r[AC][CA] = 1;r[AC][AC] = Cn + n - 3;r[AC][CC] = n - 3;r[CA][BA] = r[CA][CB] = r[CA][AC] = 1;r[CA][CA] = Cn + n - 3;r[CA][CC] = n - 3;r[BC][BA] = r[BC][AC] = r[BC][CB] = 1;r[BC][BC] = Cn + n - 3;r[BC][CC] = n - 3;r[CB][AB] = r[CB][CA] = r[CB][BC] = 1;r[CB][CB] = Cn + n - 3;r[CB][CC] = n - 3;r[CC][CC] = Cn + 2 * n - 7;r[CC][AC] = r[CC][CA] = r[CC][BC] = r[CC][CB] = 1;return r;
}typedef std::array<mi, 7> vector;
inline void xmul(vector& f, const matrix& matr) {static i64 tmp[7]; memset(tmp, 0, sizeof tmp);for(int k = 0; k < 7; ++k) {for(int j = 0; j < 7; ++j) {tmp[k] += (f[j] * matr[j][k]).load();}}for(int i = 0; i < 7; ++i) f[i] = tmp[i] %MOD;
}inline matrix matrpow(matrix x, int k) {matrix res; res.reset();for(int i = 0; i < 7; ++i) res[i][i] = 1;for(; k; k >>= 1, x = x * x) {if(k & 1) res = res * x;}return res;
}int n, q, k;struct _bit {i64 f[N], sum;void add(int x, i64 y) { sum += y; for(; x <= n; x += x & -x) f[x] += y; }i64 _get(int x) { i64 res = 0; for(; x; x -= x & -x) res += f[x]; return res; }int pre(int x) { return gmod(_get(x)%MOD+MOD); }int suf(int x) { return gmod((sum-_get(x-1))%MOD+MOD); }void clear() { for(int i = 1; i <= n; ++i) f[i] = 0; }void setz(int x) { for(; x <= n; x += x & -x) f[x] = 0; }
};
_bit S, T;int v[N];signed main() {std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);std::cin >> n >> q >> k;for(int i = 1; i <= n; ++i) { std::cin >> v[i]; }vector a; for(int i = 0; i < 7; ++i) a[i] = i == 0;const matrix F = build(n); xmul(a, matrpow(F, k-q)); std::vector<vector> prob; prob.reserve(q+1);prob.push_back(a); for(int i = 1; i <= q; ++i) xmul(a, F), prob.push_back(a);static vector coef[N];struct node {int t, p, v; mi co;node(int _t, int _p, int _v, int _c): t(_t), p(_p), v(_v), co(_c) { }};std::vector<node> w; w.reserve(n + 4 * q);for(int i = 1; i <= n; ++i) w.emplace_back(0, i, v[i], 1);for(int i = 1; i <= q; ++i) {int x, y; std::cin >> x >> y; if(x > y) std::swap(x, y);w.emplace_back(i, x, v[x], MOD-1), w.emplace_back(i, y, v[y], MOD-1);std::swap(v[x], v[y]), w.emplace_back(i, x, v[x], 1), w.emplace_back(i, y, v[y], 1);}static auto insert = [](int x, mi co) { T.add(x, co.load()), S.add(x, 1ull * x * co.load()); };static auto setz = [](int x) { T.setz(x), S.setz(x); };static auto clear = []() { T.clear(), S.clear(); };int OBT = n / std::__lg(n);std::function<void(int, int)> cdq = [&](int l, int r) -> void {if(r <= l + 1) return ;int m = (l + r) >> 1;cdq(l, m), cdq(m, r);int j = l;for(int i = m; i < r; ++i) {while(j < m && w[j].p < w[i].p) insert(w[j].v, w[j].co), ++j;auto& CO = coef[w[i].t]; mi& C = w[i].co;mi c0 = C * T.pre(w[i].v - 1), d0 = C * S.pre(w[i].v - 1);CO[BA] += c0, CO[AC] += (d0 - c0), CO[CA] += (c0 * (n-1) - d0);CO[BC] += c0 * (w[i].v - 2), CO[CB] += c0 * (n - w[i].v);mi c1 = C * T.suf(w[i].v + 1), d1 = C * S.suf(w[i].v + 1);CO[AB] += c1, CO[AC] += (d1 - 2 * c1), CO[CA] += (c1 * n - d1);CO[BC] += c1 * (w[i].v - 1), CO[CB] += c1 * (n - w[i].v - 1);} T.sum = S.sum = 0; if(j - l > OBT) clear();else while(j > l) setz(w[--j].v);j = m-1;for(int i = r-1; i >= m; --i) {while(j >= l && w[j].p > w[i].p) insert(w[j].v, w[j].co), --j;auto& CO = coef[w[i].t]; mi& C = w[i].co;mi c0 = C * T.pre(w[i].v - 1), d0 = C * S.pre(w[i].v - 1);CO[AB] += c0, CO[BC] += (d0 - c0), CO[CB] += (c0 * (n-1) - d0);CO[AC] += c0 * (w[i].v - 2), CO[CA] += c0 * (n - w[i].v);mi c1 = C * T.suf(w[i].v + 1), d1 = C * S.suf(w[i].v + 1);CO[BA] += c1, CO[BC] += (d1 - 2 * c1), CO[CB] += (c1 * n - d1);CO[AC] += c1 * (w[i].v - 1), CO[CA] += c1 * (n - w[i].v - 1);} T.sum = S.sum = 0; if(m - j > OBT) clear();else while(j + 1 < m) setz(w[++j].v);std::inplace_merge(w.begin() + l, w.begin() + m, w.begin() + r, [](auto&& x, auto&& y) { return x.p < y.p; });};cdq(0, w.size());for(int i = 1; i <= q; ++i) {for(int j = 0; j < 7; ++j) {coef[i][j] += coef[i-1][j];}}const mi inv2 = fpow(mi(2), MOD-2), invn = fpow(mi(n-2), MOD-2);auto prod = [&](const vector& co, const vector& pr) {mi res = 0;res += co[BA] * pr[BA] + co[AB] * pr[AB];res += (co[AC] * pr[AC] + co[CA] * pr[CA] + co[BC] * pr[BC] + co[CB] * pr[CB]) * invn;res += pr[CC] * C(n) * inv2;return res;};mi Cn = C(n), inv1 = fpow(fpow(Cn, MOD-2), k);for(int i = 0; i <= q; ++i) {std::cout << (prod(coef[i], prob[q-i]) * inv1).load() << "\n";inv1 *= Cn;}
}