题意简述
给定一个长度为 \(n\) 序列和 \(m\) 次操作,每次操作交换一对位置上的元素。
现在每一个操作可以做也可以不做,求所有操作方案得到的最终序列的逆序对个数和。
\(n,m\le 3000\)。
算法分析
直接做没有头绪,考虑期望化。
期望有一个很重要的线性性质,可以将问题独立。我们考虑每一对逆序对的单元是一对数,因此我们需要维护所有二元组 \(\left(x,y\right)\) 满足 \(a_x<a_y\) 的概率。
这样对于一次交换操作的做或不做,只会影响到与当前操作相关的二元组,这些二元组要么包含第一个数,要么包含第二个数,总量为 \(O(n)\) ,可以接受。
注意最后将概率和乘上 \(2^m\) 。
总时间复杂度为 \(O(n^2+nm)\) 。
代码实现
#include<bits/stdc++.h>
using namespace std;
#define maxn 3005
#define maxm 2000005
#define inf 0x3f3f3f3f
#define int long long
#define mod 1000000007
#define local
void file(string s){freopen((s+".in").c_str(),"r",stdin);freopen((s+".out").c_str(),"w",stdout);}
template <typename Tp> void read(Tp &x){
int fh=1;char c=getchar();x=0;
while(c>'9'||c<'0'){if(c=='-'){fh=-1;}c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c&15);c=getchar();}x*=fh;
}
int n,m;
int a[maxn];
int dp[maxn][maxn];
int ksm(int b,int p){int ret=1;while(p){if(p&1)ret=1ll*ret*b%mod;b=1ll*b*b%mod;p>>=1;}return ret;}
signed main(){
read(n);read(m);
for(int i=1;i<=n;++i)read(a[i]);
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
dp[i][j]=(a[i]<a[j]);
}
}
for(int i=1,x,y;i<=m;++i){
read(x);read(y);
dp[x][y]=dp[y][x]=1ll*ksm(2,mod-2)*(dp[x][y]+dp[y][x])%mod;//两者交换,等概率出现自身与对方的情况
for(int j=1;j<=n;++j){
if(j!=x&&j!=y){//下面两种同理
dp[x][j]=dp[y][j]=1ll*ksm(2,mod-2)*(dp[x][j]+dp[y][j])%mod;
dp[j][x]=dp[j][y]=1ll*ksm(2,mod-2)*(dp[j][x]+dp[j][y])%mod;
}
}
}
int ans=0;
for(int i=1;i<=n;++i){
for(int j=1;j<i;++j){
ans=(ans+dp[i][j])%mod;
}
}
ans=1ll*ans*ksm(2,m)%mod;
printf("%lld\n",ans);
return 0;
}