[题解] [AGC030D] Inversion Sum
题面
题解
如果我们知道在这 \(2^p\) 种方案中, 每一对 \(i, j\) 满足 \(i < j\) 且 \(a_i > a_j\) 的方案数
那么我们把所有的加起来就行了
考虑 DP 这样一个东西
\(f[i][j]\) 为 \(i\) 位置上的数大于 \(j\) 位置上的数的概率
假设每一次操作有 \(\frac{2}{1}\) 的概率进行, 否则不进行
那么 \(q\) 次操作完之后 \(f[i][j] * 2 ^ p\) 就是 \(i\) 位置上的数大于 \(j\) 位置上的数的期望方案的个数
然后考虑一次操作 \(x, y\), 只会改变 \(O(n)\) 个状态
转移方程是
\[f[x][i] = (f[x][i] + f[y][i]) / 2
\]
自己想一想为什么
复杂度为 \(O(n * q)\)
妙啊
Code
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
const int N = 3e3 + 5;
const int mod = 1e9 + 7;
const int inv = 5e8 + 4;
using namespace std;
int n, q, a[N], f[N][N], ans;
template < typename T >
inline T read()
{
T x = 0, w = 1; char c = getchar();
while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * w;
}
int fpow(int x, int y)
{
int res = 1;
for( ; y; y >>= 1, x = 1ll * x * x % mod)
if(y & 1) res = 1ll * res * x % mod;
return res;
}
int main()
{
n = read <int> (), q = read <int> ();
for(int i = 1; i <= n; i++)
a[i] = read <int> ();
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
f[i][j] = a[i] > a[j];
for(int x, y, i = 1; i <= q; i++)
{
x = read <int> (), y = read <int> ();
f[x][y] = f[y][x] = 1ll * (f[x][y] + f[y][x]) % mod * inv % mod;
for(int j = 1; j <= n; j++)
{
if(j == x || j == y) continue;
f[x][j] = f[y][j] = 1ll * (f[x][j] + f[y][j]) % mod * inv % mod;
f[j][x] = f[j][y] = 1ll * (f[j][x] + f[j][y]) % mod * inv % mod;
}
}
for(int i = n; i >= 1; i--)
for(int j = i - 1; j >= 1; j--)
ans = (ans + f[j][i]) % mod;
printf("%lld\n", 1ll * ans * fpow(2, q) % mod);
return 0;
}