说在前面
——好吧,这矩阵十分特殊,me没有想到
题目
题目大意
现在有
个球,每个球上有一个标号。标号为
的球有
个
现在进行
次如下的操作:
随机选取一个球,将其标号+1,如果标号大于
,则对
取模
现在询问操作之后,每种标号的球期望有多少个
范围:
输入输出格式
输入格式:
第一行三个整数
,含义如题
接下来一行
个整数,第
个数为
输出格式:
输出
个小数,每行一个,第
行表示最终标号为
的球的期望个数。保留三位小数
解法
首先很显然的有一个dp式子:
然后构造矩阵尝试优化转移,然后矩阵长这样:
但是乘一次就是
,显然GG
然后我们可以发现,它无论怎么乘,第
行都是第
行循环右移;第
列都是第
列循环下移
所以我们只需要维护矩阵中任意一行的权值,就可以知道整个矩阵
于是乘一次变成了
,可过
下面是代码
#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() ;
}