题目链接
Atcoder方向
Luogu方向
题目解法
妙妙题!!!
考虑一个神奇的转化,把方案求和变成求 i , j i,j i,j 位置是逆序对的概率
最后的答案就是 2 q ∗ ∑ f i , j 2^q*\sum f_{i,j} 2q∗∑fi,j
考虑 f i , j f_{i,j} fi,j 为 i , j i,j i,j 为逆序对的概率
考虑每次交换 x , y x,y x,y 只会修改 f x , ? , f ? , x , f y , ? , f ? , y f_{x,?},\;f_{?,x},\;f_{y,?},\;f_{?,y} fx,?,f?,x,fy,?,f?,y,一共 O ( n ) O(n) O(n) 个值
转移可以考虑交换不交换的概率都是 1 2 \frac{1}{2} 21
需要对于 f x , y , f y , x f_{x,y},\;f_{y,x} fx,y,fy,x 特判,不过这都是细节,主要还是把求和转化为求概率这一关键步骤
时间复杂度 O ( n q ) O(nq) O(nq)
#include <bits/stdc++.h>
using namespace std;
const int N=3100,P=1e9+7,iv2=500000004;
int n,q,a[N],f[N][N];
inline int read(){int FF=0,RR=1;char ch=getchar();for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;return FF*RR;
}
int main(){n=read(),q=read();for(int i=1;i<=n;i++) a[i]=read();for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) f[i][j]=a[i]>a[j];for(int i=1;i<=q;i++){int x=read(),y=read();for(int i=1;i<=n;i++){if(i==x||i==y) continue;int val=(1ll*f[x][i]*iv2+1ll*f[y][i]*iv2)%P;f[x][i]=f[y][i]=val;val=(1ll*f[i][x]*iv2+1ll*f[i][y]*iv2)%P;f[i][x]=f[i][y]=val;}int val=(1ll*f[x][y]*iv2+1ll*f[y][x]*iv2)%P;f[x][y]=f[y][x]=val;}int ans=0;for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) ans=(ans+f[i][j])%P;for(int i=1;i<=q;i++) ans=ans*2%P;printf("%d",ans);return 0;
}
/*
f[i][j]:a[i]>a[j]的期望
*/