#4229. Inverse

题目描述

小C有一个 1 1 1 n n n 的排列 P P P,他会进行 k k k 次操作,每次等概率选择一段连续区间(每次有 n ( n + 1 ) 2 \frac{n(n+1)}{2} 2n(n+1) 种选择),然后翻转这个区间。

小C想知道 k k k 次操作后逆序对的期望个数,他觉得这实在是个一眼题,于是这个任务就交给你了。

为了避免精度误差,你只需要输出期望在模 1 0 9 + 7 10^9 + 7 109+7 意义下的结果。

题解

考虑 ( i , j ) (i,j) (i,j) 的贡献,于是设计dp: f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k] 表示 ( i , j ) (i,j) (i,j) k k k 轮之后 p i > p j p_i>p_j pi>pj 的概率, g [ i ] [ j ] [ k ] g[i][j][k] g[i][j][k] 表示 p i < p j p_i<p_j pi<pj的概率,其中 i < j i<j i<j

考虑 f f f 的转移,假设第 k k k 轮翻转 [ l , r ] [l,r] [l,r] ,设 U = 2 n ( n − 1 ) U=\frac{2}{n(n-1)} U=n(n1)2 ,分类一下:

  1. l ∈ [ 1 , i ] , r ∈ [ i , j ) l \in [1,i],r \in [i,j) l[1,i],r[i,j) ,贡献为 U × f [ l + r − i ] [ j ] [ k − 1 ] U \times f[l+r-i][j][k-1] U×f[l+ri][j][k1]
  2. l ∈ ( i , j ] , r ∈ [ j , n ] l \in (i,j],r \in [j,n] l(i,j],r[j,n] ,贡献为 U × f [ i ] [ l + r − j ] [ k − 1 ] U \times f[i][l+r-j][k-1] U×f[i][l+rj][k1]
  3. l ∈ ( i , j ) , r ∈ ( i , j ) ∣ ∣ l ∈ ( j , n ] ∣ ∣ r ∈ [ 1 , i ) l \in (i,j),r \in (i,j) || l \in (j,n] || r \in [1,i) l(i,j),r(i,j)l(j,n]r[1,i) ,贡献为 U × f [ i ] [ j ] [ k − 1 ] U \times f[i][j][k-1] U×f[i][j][k1]
  4. l ∈ [ 1 , i ] , r ∈ [ j , n ] l \in [1,i],r \in [j,n] l[1,i],r[j,n] ,贡献为 U × g [ l + r − j ] [ l + r − i ] [ k − 1 ] U \times g[l+r-j][l+r-i][k-1] U×g[l+rj][l+ri][k1]

g g g 的转移是类似的。

对于前两个来说,我们发现有一维是固定的,所以另一维做二次前缀和即可,对于第四个来说,我们发现它的差是固定的,所以对于差我们做二次前缀和即可,因此效率为 O ( n 2 k ) O(n^2k) O(n2k)

代码
#include <bits/stdc++.h>
using namespace std;
const int N=505,P=1e9+7;
int f[2][N][N],g[2][N][N][3],h[2][N][N][3],s[2][N][N][3],a[N],n,m,V,ans,F[2][N][N];
inline int X(int x){return x>=P?x-P:x;}
int K(int x,int y){
	int z=1;
	for (;y;y>>=1,x=1ll*x*x%P)
		if (y&1) z=1ll*z*x%P;
	return z;
}
inline int H(int i,int j,int o){
	int u,v,x,y;
	v=1ll*h[o][j-1][j][2]*i%P;
	x=X(h[o][i][j][1]-1ll*h[o][i][j][2]*(n-i)%P+P);
	y=X(X(h[o][j-1][j][0]-h[o][j-i-1][j][0]+P)-1ll*(h[o][j-1][j][2]-h[o][j-i-1][j][2]+P)*(j-i)%P+P);
	u=X(v+P-X(x+y));
	
	v=1ll*g[o][i][n][2]*(j-i)%P;
	x=X(g[o][i][j][1]-1ll*g[o][i][j][2]*(n-j)%P+P);
	y=X(X(g[o][i][n][0]-g[o][i][n+i-j][0]+P)-1ll*(g[o][i][n][2]-g[o][i][n+i-j][2]+P)*(n+i-j+1)%P+P);
	u=X(u+X(v+P-X(x+y)));
	
	o^=1;
	v=1ll*s[o][n+i-j][j-i][2]*i%P;
	x=X(1ll*s[o][i][j-i][1]-1ll*s[o][i][j-i][2]*(n-i)%P+P);
	y=X(X(s[o][n+i-j][j-i][0]-s[o][n-j][j-i][0]+P)-1ll*(s[o][n+i-j][j-i][2]-s[o][n-j][j-i][2]+P)*(n-j+1)%P+P);
	return X(u+X(v+P-X(x+y)));
}
int main(){
	cin>>n>>m;V=X(K(n*(n+1),P-2)<<1);
	for (int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for (int i=1;i<=n;i++)
		for (int j=i+1;j<=n;j++)
			f[a[i]<a[j]][i][j]=1;
	for (int k=1;k<=m;k++){
		for (int o=0;o<2;o++)
			for (int i=1;i<=n;i++){
				for (int j=i+1;j<=n;j++)
					g[o][i][j][0]=X(g[o][i][j-1][0]+1ll*j*f[o][i][j]%P),
					g[o][i][j][1]=X(g[o][i][j-1][1]+1ll*(n-j)*f[o][i][j]%P),
					g[o][i][j][2]=X(g[o][i][j-1][2]+f[o][i][j]),
					s[o][i][j-i][0]=X(s[o][i-1][j-i][0]+1ll*i*f[o][i][j]%P),
					s[o][i][j-i][1]=X(s[o][i-1][j-i][1]+1ll*(n-i)*f[o][i][j]%P),
					s[o][i][j-i][2]=X(s[o][i-1][j-i][2]+f[o][i][j]);
				for (int j=1;j<i;j++)
					h[o][j][i][0]=X(h[o][j-1][i][0]+1ll*j*f[o][j][i]%P),
					h[o][j][i][1]=X(h[o][j-1][i][1]+1ll*(n-j)*f[o][j][i]%P),
					h[o][j][i][2]=X(h[o][j-1][i][2]+f[o][j][i]);
			}
		for (int o=0;o<2;o++){
			for (int i=1;i<=n;i++)
				for (int j=i+1;j<=n;j++)
					f[o][i][j]=1ll*(h[o][i][j][2]-h[o][i-1][j][2]+P)*(((j-i-1)*(j-i)+(i-1)*i+(n-j)*(n-j+1))>>1)%P,
					f[o][i][j]=1ll*V*X(f[o][i][j]+H(i,j,o))%P;
		}
	}
	for (int i=1;i<=n;i++)
		for (int j=i+1;j<=n;j++)
			ans=X(ans+f[0][i][j]);
	cout<<ans<<endl;return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值