bzoj4204 取球游戏

来源:互联网 发布:剑三大师捏脸数据 编辑:程序博客网 时间:2024/06/09 00:26

题目大意

有n堆球,球的数量为a[1],a[2],a[3],……,a[n].其中ni=1a[i]=m现在进行k轮操作,每一轮操作从m个球中随机选择一个,设其编号为p,将该球放入第(p+1)堆中(当p=n时,放入第1堆中);问:k轮操作完成后,每一堆球个数的期望

数据范围

n<=1000,m<=10^8,k<=2147483647.

题解

容易知道,如果上一轮完成后期望为a[i],现在期望为b[i]则有:
b[i]=1ma[lsti]+(11m)a[i]lsti=i1(i>1),lst1=n
显然可以矩阵乘法。
但是O(n3log k) 过不了.
我们发现转移矩阵是循环的,每一行都可以由第一行平移得到。于是我们只存储和转移第一行即可,其余位置的如果需要用到,则可以简单映射到第一行的相应位置.

Code

#include<cstdio>#include<cstring>using namespace std;#define MAXN 1100int n,m,k;double A[MAXN],B[MAXN],C[MAXN],D[MAXN];void _r(int& x){    char c=getchar();    while(c<'0'||c>'9')    {        c=getchar();    }    for(x=0;c>='0'&&c<='9';c=getchar())    {        x=(x<<1)+(x<<3)+c-'0';    }    return ;}void work(double x[],double y[],double p[]){    memset(D,0,sizeof(D));    for(int i=0;i<n;i++)    {        D[i]=0;        for(int j=0;j<n;j++)        {            D[i]+=x[j]*y[(i-j+n)%n];        }    }    memcpy(p,D,sizeof(D));    return ;}void ksm(int k){    C[0]=1.0;    for(;k;k>>=1)    {        if(k&1)        {            work(B,C,C);        }        work(B,B,B);    }    return ;}int main(){    //freopen("ball.in","r",stdin);    //freopen("ball.out","w",stdout);    _r(n);    _r(m);    _r(k);    for(int i=0,x;i<n;i++)    {        _r(x);        A[i]=1.0*x;    }    B[0]=1.0-1.0/m;    B[1]=1.0/m;    ksm(k);    double ans=0;    for(int i=0;i<n;i++)    {        ans=0;        for(int j=0;j<n;j++)        {            ans+=1.0*A[j]*C[(i-j+n)%n];        }        printf("%.3lf\n",ans);    }    return 0;}

第一次写循环矩阵的矩阵乘法,写得有点丑!