题意
有M个球,一开始每个球均有一个初始标号,标号范围为1~N且为整数,标号为i的球有ai个,并保证Σai = M。
每次操作等概率取出一个球(即取出每个球的概率均为1/M),若这个球标号为k(k < N),则将它重新标号为k + 1;若这个球标号为N,则将其重标号为1。(取出球后并不将其丢弃)
现在你需要求出,经过K次这样的操作后,每个标号的球的期望个数。
N ≤ 1000, M ≤ 100,000,000, K ≤ 2,147,483,647。
分析
如果设f[i,j]表示j次操作后标号为i的球的期望数量,不难得到f[i,j]=f[i,j-1]*(m-1)/m+f[i-1,j-1]/m。
如果n是100的话,就可以直接上矩阵快速幂了。但现在的n是等于1000。
观察状态转移矩阵,不难发现这是一个循环矩阵,也就是其行与行之间或列与列之间都是循环同构的。手玩一下不难发现由两个循环的矩阵乘得的矩阵还是循环的,那么我们就可以只记录循环矩阵的第一行,就可以O(n^2)log得到最终矩阵了。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1005;
int n,m,k,w[N];
struct Matrix
{
double a[N];
void clear(int n)
{
for (int i=0;i<n;i++) a[i]=0;
}
}a;
void mul(Matrix &c,Matrix a,Matrix b)
{
c.clear(n);
for (int i=0;i<n;i++)
for (int j=0;j<n;j++)
c.a[i]+=a.a[j]*b.a[(i+n-j)%n];
}
Matrix ksm(Matrix x,int y)
{
Matrix ans;ans.clear(n);ans.a[0]=1;
while (y)
{
if (y&1) mul(ans,ans,x);
mul(x,x,x);y>>=1;
}
return ans;
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for (int i=0;i<n;i++) scanf("%d",&w[i]);
a.a[0]=(double)(m-1)/m;a.a[1]=(double)1/m;
a=ksm(a,k);
for (int i=0;i<n;i++)
{
double ans=0;
for (int j=0;j<n;j++) ans+=(double)w[j]*a.a[(i+n-j)%n];
printf("%.3lf\n",ans);
}
return 0;
}