[bzoj2510][矩阵乘法][DP]弱题

123 篇文章 1 订阅
12 篇文章 0 订阅

Description

有M个球,一开始每个球均有一个初始标号,标号范围为1~N且为整数,标号为i的球有ai个,并保证Σai = M。
每次操作等概率取出一个球(即取出每个球的概率均为1/M),若这个球标号为k(k < N),则将它重新标号为k +
1;若这个球标号为N,则将其重标号为1。(取出球后并不将其丢弃) 现在你需要求出,经过K次这样的操作后,每个标号的球的期望个数。

Input

第1行包含三个正整数N,M,K,表示了标号与球的个数以及操作次数。 第2行包含N个非负整数ai,表示初始标号为i的球有ai个。

Output

应包含N行,第i行为标号为i的球的期望个数,四舍五入保留3位小数。

Sample Input

2 3 2

3 0

Sample Output

1.667

1.333

HINT

【样例说明】

第1次操作后,由于标号为2球个数为0,所以必然是一个标号为1的球变为标号为2的球。所以有2个标号为1的球,有1个标号为2的球。

第2次操作后,有1/3的概率标号为2的球变为标号为1的球(此时标号为1的球有3个),有2/3的概率标号为1的球变为标号为2的球(此时标号为1的球有1个),所以标号为1的球的期望个数为1/33+2/31
= 5/3。同理可求出标号为2的球期望个数为4/3。

【数据规模与约定】

对于10%的数据,N ≤ 5, M ≤ 5, K ≤ 10;

对于20%的数据,N ≤ 20, M ≤ 50, K ≤ 20;

对于30%的数据,N ≤ 100, M ≤ 100, K ≤ 100;

对于40%的数据,M ≤ 1000, K ≤ 1000;

对于100%的数据,N ≤ 1000, M ≤ 100,000,000, K ≤ 2,147,483,647。

题解

并不知道这个东西
首先写个暴力就可以验证dp方程是
f [ i ] [ j ] = f [ i − 1 ] [ j ] ∗ m − 1 m + f [ i − 1 ] [ p r e ] ∗ 1 m f[i][j]=f[i-1][j]*\frac{m-1}{m}+f[i-1][pre]*\frac{1}{m} f[i][j]=f[i1][j]mm1+f[i1][pre]m1
这个显然可以写成一个矩乘的形式…
然后他是 n 3 l o g K n^3logK n3logK的…
并不知道矩阵还有另一种方法优化到 n 2 n^2 n2,所以跪了
在这里插入图片描述
上图是转移矩阵,他一个循环矩阵
注意到下面一行都是由上一行向右移一格得到的…
那么有一个性质,循环矩阵的次幂同样是一个循环矩阵
所以我们只需要保存第一行的状态是什么,朴素矩乘是
f [ i ] [ k ] = ∑ a [ i ] [ j ] ∗ b [ j ] [ k ] f[i][k]=\sum a[i][j]*b[j][k] f[i][k]=a[i][j]b[j][k]
这里矩乘的形式推一下就可以变为
f [ k ] = ∑ a [ k ] ∗ b [ k − j + 1 ] f[k]=\sum a[k]*b[k-j+1] f[k]=a[k]b[kj+1]
其中 k − j + 1 k-j+1 kj+1 [ 1 , n ] [1,n] [1,n]中,如果小于1了就加个 n n n
那么快速幂就可以变为 n 2 l o g k n^2logk n2logk的,出来再做个朴素矩乘就可以了

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline int read()
{
    int f=1,x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int stack[20];
inline void write(LL x)
{
    if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(LL x){write(x);putchar('\n');}
const int MAXN=1005;
int n,m,K;
struct ma1
{
    double m[MAXN];
    ma1(){memset(m,0,sizeof(m));}
}st,tp1;
struct ma2
{
    double m[MAXN][MAXN];
    ma2(){memset(m,0,sizeof(m));}
}tp2;
ma1 mul1(ma1 u1,ma1 u2)
{
    ma1 ret;
    for(int k=1;k<=n;k++)
        for(int j=1;j<=n;j++)
        {
            int lst=k-j+1;if(lst<1)lst+=n;
            ret.m[k]+=u1.m[j]*u2.m[lst];
        }
    return ret;
}
ma1 mul2(ma2 u2,ma1 u1)
{
    ma1 ret;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
        ret.m[i]+=u2.m[i][j]*u1.m[j];
    return ret;
}
ma1 pow_mod(ma1 u1,int b)
{
    ma1 ret=u1;b--;
    while(b)
    {
        if(b&1)ret=mul1(ret,u1);
        u1=mul1(u1,u1);b>>=1;
    }
    return ret;
}
int main()
{
//  freopen("2.in","r",stdin);
    n=read();m=read();K=read();
    for(int i=1;i<=n;i++)st.m[i]=read();
    tp1.m[1]=(double)(m-1)/m;tp1.m[n]=(double)1/m;
    tp1=pow_mod(tp1,K);
//  printf("CHECKER\n");
    for(int i=1;i<=n;i++)
    {
        int st=i;
        for(int j=1;j<=n;j++)
        {
            tp2.m[i][st]=tp1.m[j];
//          printf("%.3lf ",tp2.m[i][j]);
            st++;if(st>n)st=1;
        }
//      puts("");
    }
//  puts("");
     
    st=mul2(tp2,st);
    for(int i=1;i<=n;i++)printf("%.3lf\n",st.m[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值