【Atcoder】 [AGC030D] Inversion Sum

题目链接

Atcoder方向
Luogu方向

题目解法

妙妙题!!!
考虑一个神奇的转化,把方案求和变成求 i , j i,j i,j 位置是逆序对的概率
最后的答案就是 2 q ∗ ∑ f i , j 2^q*\sum f_{i,j} 2qfi,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 
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]的期望
*/

你可能感兴趣的:(Atcoder,算法)