https://www.luogu.com.cn/problem/AT4513
像这种一个操作做或不做,总方案数特别大,求所有方案数满足什么条件的可以往概率方面想
交换与否的概率是等价的,所以可以先做一个概率DP
f
[
i
]
[
j
]
表
示
做
到
目
前
这
个
操
作
,
a
[
i
]
>
a
[
j
]
的
概
率
f[i][j]表示做到目前这个操作,a[i]>a[j]的概率
f[i][j]表示做到目前这个操作,a[i]>a[j]的概率
最后求和再乘上
2
Q
\large 2^Q
2Q就是答案了
转移也十分显然,每次操作
x
,
y
x, y
x,y,只有当
i
,
j
i,j
i,j其中一个为
x
,
y
x,y
x,y的其中一个的
D
P
DP
DP值才会受影响,所以直接转移就好了
看代码理解一下吧
code:
#include<bits/stdc++.h>
#define mod 1000000007
#define N 3005
#define ll long long
using namespace std;
const int inv = (mod + 1) / 2;
int n, q, a[N];
ll f[N][N], g[N][N];
int main() {
scanf("%d%d", &n, &q);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= n; j ++)
f[i][j] = a[i] > a[j];
ll ha = 1;
while(q --) {
int x, y;
scanf("%d%d", &x, &y);
if(x > y) swap(x, y);
for(int i = 1; i <= n; i ++) if(i != x && i != y) {//转移
g[i][x] = (ll) inv * (f[i][x] + f[i][y]) % mod;
g[i][y] = (ll) inv * (f[i][x] + f[i][y]) % mod;
g[x][i] = (ll) inv * (f[x][i] + f[y][i]) % mod;
g[y][i] = (ll) inv * (f[x][i] + f[y][i]) % mod;
}
// for(int i = 1; i <= n; i ++) {
// for(int j = 1; j <= n; j ++) printf("%lld ", f[i][j]); printf("\n");
// } printf("\n");
for(int i = 1; i <= n; i ++) if(i != x && i != y) {
f[i][x] = g[i][x];
f[i][y] = g[i][y];
f[x][i] = g[x][i];
f[y][i] = g[y][i];
}
f[x][y] = f[y][x] = (ll) inv * (f[x][y] + f[y][x]) % mod;//记得要考虑这种情况
ha = ha * 2 % mod;
}
ll ans = 0;
for(int i = 1; i <= n; i ++)
for(int j = i + 1; j <= n; j ++)
ans = (ans + f[i][j]) % mod;
printf("%lld", ans * ha % mod);
return 0;
}
思维还是要打开一点,这种题做了好多次了,要多思考
记得计数可以往概率方面想