jzoj6038 Inverse 概率dp

11 篇文章 0 订阅

Description


给定一个长度为n的排列a[]进行k次操作,每次等概率选择一个连续区间进行翻转操作,问k次操作后序列逆序对的期望数量
n ≤ 500 ,    k ≤ 50 n\le500,\;k\le50 n500,k50

Solution


大力讨论题,之前做过一个k次操作是给定的,感觉差不多吧
一个非常naive的dp就是 f [ i , j , k ] f[i,j,k] f[i,j,k]表示k次操作,i<j的概率,然后我们暴力枚举所有可能的操作区间转移,总的复杂度是 O ( n 4 k ) O(n^4k) O(n4k)的,理论上有39p(选手实现的差异产生了分数的区别

转移如下:

  1. r &lt; i 或 l &gt; j 或 i &lt; l ≤ r &lt; j r&lt;i或l&gt;j或i&lt;l\le r&lt;j r<il>ji<lr<j,这三种情况无论怎么翻转都不影响答案
  2. l ≤ i ≤ r &lt; j l\le i\le r&lt; j lir<j,这种情况第i个数变成了第 r + l − i r+l-i r+li
  3. i &lt; l ≤ j ≤ r i&lt;l\le j\le r i<ljr,这种情况第j个数变成了第 r + l − j r+l-j r+lj
  4. l ≤ i &lt; j ≤ r l\le i&lt;j\le r li<jr,这种情况i和j对应都变了

考虑转移1怎么优化,系数就是不包含i和j两个位置的子区间数量,这样转移就是O(1)的了
考虑转移2怎么优化,我们实际上在求这个东西 ∑ l = 1 i ∑ r = i j − 1 f [ r + l − i , j , k − 1 ] \sum\limits_{l=1}^{i}\sum\limits_{r=i}^{j-1}f[r+l-i,j,k-1] l=1ir=ij1f[r+li,j,k1],那么就是两次前缀和了
转移3和2是类似的
然后我做到这就不会了。。。

转移4实际上在求这个东西 ∑ l = 1 i ∑ r = j n f [ r + l − j , r + l − i , k − 1 ] \sum\limits_{l=1}^i\sum\limits_{r=j}^n{f[r+l-j,r+l-i,k-1]} l=1ir=jnf[r+lj,r+li,k1]
注意到这里两维的差是常数,那么令 g [ i , j ] = f [ i , i + j , k − 1 ] g[i,j]=f[i,i+j,k-1] g[i,j]=f[i,i+j,k1],于是变成求 ∑ l = 1 i ∑ r = j n g [ r + l − j , j − i ] \sum\limits_{l=1}^i\sum\limits_{r=j}^n{g[r+l-j,j-i]} l=1ir=jng[r+lj,ji],就和上面一样了

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (register int i=st;i<=ed;++i)

typedef long long LL;
const int MOD=1000000007;
const int ny2=(MOD+1)/2;
const int N=505;

LL s1[N][N],s2[N][N],s3[N][N];
LL f[51][N][N],g[N][N],wjp;

int a[N];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

LL ksm(LL x,LL dep) {
	LL res=1; x%=MOD;
	for (;dep;dep>>=1) {
		(dep&1)?(res=res*x%MOD):0;
		x=x*x%MOD;
	}
	return res;
}

void upd(LL &x,LL y) {
	x+=y; (x>=MOD)?(x-=MOD):0;
}

int main(void) {
	freopen("data.in","r",stdin);
	freopen("myp.out","w",stdout);
	int n=read(),m=read();
	rep(i,1,n) a[i]=read();
	rep(i,1,n) rep(j,i+1,n) f[0][i][j]=(a[i]>a[j]);
	wjp=ksm((n+1)*n/2,MOD-2);
	rep(k,1,m) {
		rep(j,1,n) {
			rep(i,1,j-1) s1[i][j]=(s1[i-1][j]+f[k-1][i][j])%MOD;
			rep(i,1,j-1) upd(s1[i][j],s1[i-1][j]);
		}
		rep(i,1,n) {
			rep(j,i+1,n) s2[i][j]=(s2[i][j-1]+f[k-1][i][j])%MOD;
			rep(j,i+1,n) upd(s2[i][j],s2[i][j-1]);
			rep(j,i+1,n) f[k][i][j]=f[k-1][i][j]*ny2%MOD*(n*n+2*i*i+2*j*j+n-2*j-2*n*j-2*i*j)%MOD;
		}
		rep(j,0,n) {
			rep(i,1,n-j) s3[i][j]=(s3[i-1][j]+f[k-1][i][i+j])%MOD;
			rep(i,1,n-j) upd(s3[i][j],s3[i-1][j]);
		}
		rep(i,1,n) rep(j,i+1,n) {
			upd(f[k][i][j],(s2[i][n]-s2[i][n-j+i]-s2[i][j-1]+s2[i][i])%MOD+MOD);
			upd(f[k][i][j],(s1[j-1][j]-s1[j-i-1][j]-s1[i-1][j])%MOD+MOD);
			upd(f[k][i][j],MOD-(s3[n-j+i][j-i]-s3[n-j][j-i]-s3[i-1][j-i])%MOD);
			upd(f[k][i][j],i*(n-j+1));
			f[k][i][j]=(f[k][i][j]*wjp%MOD+MOD)%MOD;
		}
	}
	LL ans=0;
	rep(i,1,n) rep(j,i+1,n) upd(ans,f[m][i][j]);
	printf("%lld\n", ans);
	return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值