[BZOJ2510]-弱题-dp+性质优化矩乘

说在前面

——好吧,这矩阵十分特殊,me没有想到


题目

BZOJ2510传送门

题目大意

现在有 m 个球,每个球上有一个标号。标号为 i 的球有 a i
现在进行 K 次如下的操作:
随机选取一个球,将其标号+1,如果标号大于 n ,则对 n 取模
现在询问操作之后,每种标号的球期望有多少个

范围: n 1000 , K 2147483647 , m 10 8

输入输出格式

输入格式:
第一行三个整数 n , m , K ,含义如题
接下来一行 n 个整数,第 i 个数为 a i

输出格式:
输出 n 个小数,每行一个,第 i 行表示最终标号为 i 的球的期望个数。保留三位小数


解法

首先很显然的有一个dp式子: d p i = ( 1 1 m ) d p i + 1 m d p i 1
然后构造矩阵尝试优化转移,然后矩阵长这样:
这里写图片描述但是乘一次就是 n 3 ,显然GG

然后我们可以发现,它无论怎么乘,第 i 行都是第 i 1 行循环右移;第 i 列都是第 i 1 列循环下移
所以我们只需要维护矩阵中任意一行的权值,就可以知道整个矩阵
于是乘一次变成了 n 2 ,可过


下面是代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

int N , M , K ;
struct Mat{
    double v[1005] ;
    Mat(){ memset( v , 0 , sizeof( v ) ) ; }
    Mat operator * ( const Mat &A ) const {
        Mat rt ;
        for( int i = 1 ; i <= N ; i ++ ){
            double tmp = 0 ;
            for( int j = 1 ; j <= N ; j ++ )
                tmp += v[j] * A.v[ (i-j+N)%N+1 ] ;
            rt.v[i] = tmp ;
        } return rt ;
    }
} a ;

Mat Mat_s_pow( Mat x , int b ){
    Mat rt ; rt.v[1] = 1 ;
    while( b ){
        if( b&1 ) rt = rt * x ;
        x = x * x , b >>= 1 ;
    } return rt ;
}

void solve(){
    Mat ori , res ;
    ori.v[1] = 1 - 1.0 / M , ori.v[2] = 1.0 / M ;
    res = Mat_s_pow( ori , K ) ;
    a = a * res ;
    for( int i = 1 ; i <= N ; i ++ )
        printf( "%.3f\n" , a.v[i] ) ;
}

int main(){
    scanf( "%d%d%d" , &N , &M , &K ) ;
    for( int i = 1 ; i <= N ; i ++ )
        scanf( "%lf" , &a.v[i] ) ;
    if( N == 1 ) printf( "%.3f\n" , a.v[1] ) , exit( 0 ) ;
    solve() ;
}

猜你喜欢

转载自blog.csdn.net/izumi_hanako/article/details/80335005