Inverse

题意

给出一个1~N的排列P,进行K次操作,每次等概率的选取一段区间(从所有 n ( n + 1 ) 2 \frac{n(n+1)}{2} 2n(n+1)中选)翻转
求K次操作后的逆序对期望个数

题解

定义d(i,j,k)为k次操作后, P i > P j P_i>P_j Pi>Pj的概率
暴力枚举区间转移是n^2的,
我们可以将区间[l,r]分类
1.[l,r]与[i,j]无交集或[i,j]包含了[l,r]:从 d ( i , j , k − 1 ) d(i,j,k-1) d(i,j,k1)转移
2.[l,r]中包含i而不包含j:从 d ( l + r − i , j , k − 1 ) d(l+r-i,j,k-1) d(l+ri,j,k1)转移
3.[l,r]中包含j而不包含i:从 d ( i , l + r − j , k − 1 ) d(i,l+r-j,k-1) d(i,l+rj,k1)转移
4.[l,r]中包含[i,j]:从 d ( l + r − i , l + r − j , k − 1 ) d(l+r-i,l+r-j,k-1) d(l+ri,l+rj,k1)转移
下面再考虑优化
第一类转移很好办,直接算出[1,i-1],[i,j],[j+1,r]的区间个数,可以预处理也可以直接等差数列求和
第二类转移就要稍微麻烦些了 (k我就懒得写了)
首先第二类转移的贡献f f = ∑ l = 1 i ∑ r = i j − 1 d ( l + r − i , j ) = ∑ l = 1 i ∑ r = 0 j − i − 1 d ( l + r , j , k ) f=\sum_{l=1}^i\sum_{r=i}^{j-1}d(l+r-i,j)=\sum_{l=1}^i\sum_{r=0}^{j-i-1}d(l+r,j,k) f=l=1ir=ij1d(l+ri,j)=l=1ir=0ji1d(l+r,j,k)
那么第一维是个连续的和,我们考虑前缀和优化(尽管光看这一步并没有时间复杂度上的优化),令 S ( n , j ) = ∑ i = 1 n d ( i , j ) S(n,j)=\sum_{i=1}^nd(i,j) S(n,j)=i=1nd(i,j),那么 f = ∑ l = 1 i S ( l + j − i − 1 , j ) − S ( l − 1 , j ) f=\sum_{l=1}^iS(l+j-i-1,j)-S(l-1,j) f=l=1iS(l+ji1,j)S(l1,j)
欸,似乎又是个连续的和(其实不化简也行),我们可以再次前缀和优化,令 S 1 ( n , j ) = ∑ i = 1 n S ( i , j ) S_1(n,j)=\sum_{i=1}^nS(i,j) S1(n,j)=i=1nS(i,j)那么
f = S 1 ( j − 1 , j ) − S 1 ( j − i − 1 , j ) − S 1 ( i − 1 , j ) f=S_1(j-1,j)-S_1(j-i-1,j)-S_1(i-1,j) f=S1(j1,j)S1(ji1,j)S1(i1,j)
其实后面还有一个 + S 1 ( 0 , j ) +S_1(0,j) +S1(0,j)

是不是所有形如 ∑ i = 1 n ∑ j = 1 m d ( i + j ) \sum_{i=1}^n\sum_{j=1}^md(i+j) i=1nj=1md(i+j)的都能够用前缀和优化呢
似乎是这样的呢

那么类似的,我们可以推出第三类转移的贡献
S ( p , q ) = ∑ j = 1 q d ( p , j ) S(p,q)=\sum_{j=1}^qd(p,j) S(p,q)=j=1qd(p,j),然后把这个S再求个和得到 S 2 S_2 S2
那么 f = S 2 ( i , n ) − S 2 ( i , i + n − j ) − S 2 ( i , j − 1 ) + S 2 ( i , i − 1 ) f=S_2(i,n)-S_2(i,i+n-j)-S_2(i,j-1)+S_2(i,i-1) f=S2(i,n)S2(i,i+nj)S2(i,j1)+S2(i,i1

第四类转移也是类似的
f = ∑ l = 1 i ∑ r = j n d ( l + r − i , l + r − j ) f=\sum_{l=1}^i\sum_{r=j}^{n}d(l+r-i,l+r-j) f=l=1ir=jnd(l+ri,l+rj)
我们令 g ( i , j ) = d ( i , i + j ) g(i,j)=d(i,i+j) g(i,j)=d(i,i+j)
注意此处j的意义不同!
那么 f = ∑ l = 1 i ∑ r = i + j n g ( l + r − i , − j ) = ∑ l = 1 i S ( l + n − i , − j ) − S ( l + j − 1 , − j ) = S 3 ( n , − j ) − S 3 ( n − i , − j ) − S 3 ( i + j − 1 , − j ) + S 3 ( j − 1 , − j ) f=\sum_{l=1}^i\sum_{r=i+j}^ng(l+r-i,-j)=\sum_{l=1}^iS(l+n-i,-j)-S(l+j-1,-j)=S_3(n,-j)-S_3(n-i,-j)-S_3(i+j-1,-j)+S_3(j-1,-j) f=l=1ir=i+jng(l+ri,j)=l=1iS(l+ni,j)S(l+j1,j)=S3(n,j)S3(ni,j)S3(i+j1,j)+S3(j1,j)
-j的处理我们平移个2就好了
最后再乘上总区数的逆元即可
那么最终的答案就是 ∑ i = 1 n ∑ j = i + 1 n d ( i , j , K ) \sum_{i=1}^n\sum_{j=i+1}^nd(i,j,K) i=1nj=i+1nd(i,j,K)

#include
#include
#include
using namespace std;
typedef long long ll;
const ll N=1005;
const ll mod=1e9+7;
ll ksm(ll x,ll y){
    ll res=1;
    while(y){
        if(y&1)
            res=1ll*res*x%mod;
        x=1ll*x*x%mod;
        y>>=1;
    }
    return res;
}
ll d[N][N],f[N][N];
ll s1[N][N],s2[N][N],s3[N][N];
ll s0[N];
int p[N];
int n,K;
inline ll add(ll x,ll y){
    ll res=x+y;
    res=(res%mod+mod)%mod;
    return res;
}
inline ll mul(ll x,ll y){
    ll res=1ll*x*y%mod;
    return res;
}
void calc(){
    for(ll i=1;i<=n;i++)
        for(ll j=i+1;j<=n;j++)
            d[j][i]=add(1,-d[i][j]);
    for(ll j=1;j<=n;j++){
        for(ll i=1;i<=n;i++)
            s1[i][j]=add(s1[i-1][j],d[i][j]);
        for(ll i=1;i<=n;i++)
            s1[i][j]=add(s1[i][j],s1[i-1][j]);
    }
    for(ll i=1;i<=n;i++){
        for(ll j=1;j<=n;j++)
            s2[i][j]=add(s2[i][j-1],d[i][j]);
        for(ll j=1;j<=n;j++)
            s2[i][j]=add(s2[i][j],s2[i][j-1]);
    }
    for(ll i=1;i<=n;i++){
        for(ll j=1;j<=n;j++)
            s3[i][j-i+n]=add(s3[i-1][j-i+n],d[i][j]);
    }
    for(ll i=1;i<=n;i++)
        for(ll j=1;j<=n;j++)
            s3[i][j-i+n]=add(s3[i][j-i+n],s3[i-1][j-i+n]);
}
void Debug(){
    for(ll i=1;i<=n;i++)
        for(ll j=1;j<=n;j++){
            printf("d[%lld][%lld]=%lld\n",i,j,d[i][j]);
        }
    for(ll i=1;i<=n;i++)
        for(ll j=1;j<=n;j++){
            printf("s1[%lld][%lld]=%lld\n",i,j,s1[i][j]);
        }
    for(ll i=1;i<=n;i++)
        for(ll j=1;j<=n;j++){
            printf("s2[%lld][%lld]=%lld\n",i,j,s2[i][j]);
        }
    for(ll i=1;i<=n;i++)
        for(ll j=-i+1;i+j<=n;j++){
            printf("s3[%lld][%lld]=%lld\n",i,j,s3[i][j+n]);
        }
}
int main()
{
    scanf("%d%d",&n,&K);
    for(ll i=1;i<=n;i++)
        scanf("%d",&p[i]);
    for(ll i=1;i<=n;i++)
        s0[i]=s0[i-1]+i;
    ll inv=ksm(s0[n],mod-2);
    for(ll i=1;i<=n;i++)
        for(ll j=1;j<=n;j++)
            d[i][j]=(p[i]>p[j]);
    for(ll k=1;k<=K;k++){
        calc();
        for(ll i=1;i<=n;i++)
            for(ll j=i+1;j<=n;j++){
                d[i][j]=mul(d[i][j],s0[i-1]+s0[j-i-1]+s0[n-j]);
                d[i][j]=add(d[i][j],s1[j-1][j]-s1[j-i-1][j]-s1[i-1][j]);
                d[i][j]=add(d[i][j],s2[i][n]-s2[i][i+n-j]-s2[i][j-1]+s2[i][i-1]);
                int z=j-i;
                d[i][j]=add(d[i][j],s3[n][-z+n]-s3[n-i][-z+n]-s3[i+z-1][-z+n]+s3[z-1][-z+n]);

            }
        for(ll i=1;i<=n;i++)
            for(ll j=1;j<=n;j++){
                d[i][j]=mul(d[i][j],inv);
            }
        //Debug();
    }
    //Debug();
    ll ans=0;
    for(ll i=1;i<=n;i++)
        for(ll j=i+1;j<=n;j++)
            ans=add(ans,d[i][j]);
    printf("%lld\n",ans);
}

你可能感兴趣的:(dp)