[题解] [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
", 1ll * ans * fpow(2, q) % mod); 
	return 0; 
}
原文地址:https://www.cnblogs.com/ztlztl/p/12796934.html