Description
给出一个数列{d},|d|=n。
依次进行a次操作1和b次操作2。
操作1:随机一个二元组(l,r)[l< r],交换d[l],d[r]
操作2:随机一个二元组(l,r)[l< r],翻转区间[l,r]
最后随机一个二元组(l,r)[l< r],求出区间[l,r]的和。
求最终答案的期望值。
n<=1000,a<=10^9,b<=10
Solution
被WorldWide_D强推了这道题~花了三节数学课搞了出来=w=
先考虑最简单的。
如果我们求出了e(i)表示i这个位置的期望值,那么它对答案的贡献就是覆盖它的区间个数,注意l不能等于r
Ans=∑i=1ne(i)∗i∗(n−i+1)−1n∗(n−1)/2
然后设E(i)表示i在进行完1操作之后i位置的期望值,那么e(i)可以用一个简单的dp转移过来。
设Fi,j表示i在进行了j次2操作之后的期望值。
枚举上一次的一个k对i进行贡献,发现区间个数是可以算的。
所有其他区间贡献完之后剩下的就是Fi,j-1转移到Fi,j的区间个数。
转移就不写了~
然后考虑第一步中,j对i的贡献。
可以写一个dp,转移显然。
F[0,j]=1,F[i,l]=∑k!=iF[k,l−1]/(n∗(n−1)/2)+F[i,j−1]∗(n−2)/n
发现每一层F只会有两种不同的取值,设为A和B。
那么
Ai=Ai−1∗[n−2n+n−2n∗(n−1)/2]+Bi−1∗1n∗(n−1)/2
Bi=Ai−1∗n−1n∗(n−1)/2+Bi−1∗n−2n
写个矩阵乘法就好了~
如果j不等于i那么用A转移,否则用B转移。
然后这道题就解决了。。。
结果这样子做精度不够高,本机对拍被卡掉了,然而上去交过掉了233
另一个解法
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef double db;
const int N=1e3+5;
int n,a,b,d[N];
db f[N][11],ans;
struct matrix{
db a[2][2];
friend matrix operator * (matrix y, matrix z) {
matrix x;memset(x.a,0,sizeof(x.a));
fo(i,0,1) fo(j,0,1) fo(k,0,1) x.a[i][j]+=y.a[i][k]*z.a[k][j];
return x;
}
}g,t;
int main() {
scanf("%d%d%d",&n,&a,&b);
fo(i,1,n) scanf("%d",&d[i]);
db nn=n*(n-1)/2;
g.a[0][0]=((n-2)*1.0/n+(n-2)*1.0/nn);
g.a[0][1]=(n-1)*1.0/nn;
g.a[1][0]=1.0/nn;
g.a[1][1]=(n-2)*1.0/n;
memcpy(t.a,g.a,sizeof(t.a));
for(a--;a;a/=2,g=g*g) if (a&1) t=t*g;
fo(i,1,n) {
fo(j,1,n) if (i!=j) f[i][0]+=d[j]*t.a[1][0];
f[i][0]+=d[i]*t.a[1][1];
}
fo(k,1,b) fo(i,1,n) {
f[i][k]=f[i][k-1];
fo(j,1,i-1) f[i][k]+=(f[j][k-1]-f[i][k-1])*min(j,n-i+1)*1.0/nn;
fo(j,i+1,n) f[i][k]+=(f[j][k-1]-f[i][k-1])*min(i,n-j+1)*1.0/nn;
}
fo(i,1,n) ans+=f[i][b]*(i*(n-i+1)-1)*1.0/nn;
printf("%.6lf\n",ans);
}