大佬
这本来是最水的题,由于我先做的其他三道,到这这有二十分钟了,就打了\(dfs\)枚举了所有情况暴力计算的。
这个题\(letitdown\)貌似推了一个\(O(n)\)的式子,我们只能\(\% \% \%\),说下具体思路,很简单。
是计算概率的做法,算出来抽到每个值是最大劳累值的概率乘上系数即可。
抽到最大劳累值为\(x\)的概率设为\(f_x\).
\[f_x=(\frac{x}{m})^k-\sum_i^{x-1} f_i
\]
用了一个我也不知道叫不叫容斥的东西。
我觉得应该可以写成容斥。
先保证这几天只能抽小于等于\(x\)的,那个分数就实现了。
因为他有至少一天抽到最大劳累值为\(k\),才能说最大劳累值为\(k\),所以要剪掉最大劳累值不为\(k\)的所有情况。
可以发现之前的\(f_i\)就是。
这东西可以靠一个前缀和来实现\(O(n)\)
code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define int long long
#define R register int
#define printf Ruusupuu = printf
using namespace std ;
typedef long long L ;
typedef long double D ;
typedef unsigned long long G ;
const int N = 5e2 + 10 ;
const int M = 1e9 + 7 ;
int Ruusupuu , ans ;
inline int read(){
int w = 0 ; bool fg = 0 ; char ch = getchar() ;
while( ch < '0' || ch > '9' ) fg |= ( ch == '-' ) , ch = getchar() ;
while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch ^ '0' ) , ch = getchar() ;
return fg ? -w : w ;
}
inline int qpow( int x , int b ){
int ans = 1 ;
while( b ){
if( b & 1 ) ans = 1ll * ans * x % M ;
x = 1ll * x * x % M ; b >>= 1 ;
} return ans ;
}
inline int tr( int a , int b ){ return 1ll * a * qpow( b , M - 2 ) % M ; }
inline int J( int a , int b ){ return a + b >= M ? a + b - M : a + b ; }
inline int S( int a , int b ){ return a - b < 0 ? a - b + M : a - b ; }
inline int T( int a , int b ){ return 1ll * a * b % M ; }
int n , m , k , w [N] , day , f [N] , last , fm ;
void sc(){
n = read() , m = read() , k = read() ; day = n - k + 1 ; fm = tr( 1 , m ) ;
if( day < 0 ){ puts( "0" ) ; exit( 0 ) ; }
for( R i = 1 ; i <= m ; i ++ ) w [i] = read() ;
}
void work(){
for( R i = 1 ; i <= m ; i ++ ){
f [i] = qpow( T( i , fm ) , k ) ;
f [i] = S( f [i] , last ) , last = J( last , f [i] ) ;
ans = J( ans , T( f [i] , w [i] ) ) ;
} printf( "%lld\n" , T ( day , ans ) ) ;
}
signed main(){
sc() ;
work() ;
return 0 ;
}
这道题我也自己想了一个做法,是\(O(n^2)\)的。
设\(f[i][j]\)是做了\(i\)天最大劳累度为\(j\)的概率,我们要求的就是\(f[k][i](i\in[1,m])\)
容易发现,他只能有之前做过劳累值为\(j\)的题,这次做的小于等于\(j\)和之前做的劳累值小于等于\(j\),这次做到劳累值为\(j\)的题。
不过需要稍微注意一下,之前做过劳累值为\(j\),这次做到\(j\),在两个情况中都被我们计算了,所以挑一个你不顺眼的不统计他就行了。
\[f[i,j]=f[i-1,j]*(\frac{j}{m})+\sum_{k=1}^{i-1}f[i-1,k]*(\frac{1}{m})
\]
注意我第二项只计算到\(i-1\)。
可以前缀和优化,但反正不是最优解,我没优化,也能过。
可以发现只和\(i-1\)有关,可以滚动数组,同上。
code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define int long long
#define R register int
#define printf Ruusupuu = printf
using namespace std ;
typedef long long L ;
typedef long double D ;
typedef unsigned long long G ;
const int N = 5e2 + 10 ;
const int M = 1e9 + 7 ;
int Ruusupuu , ans ;
inline int read(){
int w = 0 ; bool fg = 0 ; char ch = getchar() ;
while( ch < '0' || ch > '9' ) fg |= ( ch == '-' ) , ch = getchar() ;
while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch ^ '0' ) , ch = getchar() ;
return fg ? -w : w ;
}
inline int qpow( int x , int b ){
int ans = 1 ;
while( b ){
if( b & 1 ) ans = 1ll * ans * x % M ;
x = 1ll * x * x % M ; b >>= 1 ;
} return ans ;
}
inline int tr( int a , int b ){ return 1ll * a * qpow( b , M - 2 ) % M ; }
inline int J( int a , int b ){ return a + b >= M ? a + b - M : a + b ; }
inline int S( int a , int b ){ return a - b < 0 ? a - b + M : a - b ; }
inline int T( int a , int b ){ return 1ll * a * b % M ; }
int n , m , k , w [N] , day , f [N][N] , fm ;
void sc(){
n = read() , m = read() , k = read() ; day = n - k + 1 , fm = tr( 1 , m ) ;
if( day < 0 ) puts( "0" ) , exit( 0 ) ;
for( R i = 1 ; i <= m ; i ++ ) w [i] = read() ;
}
void work(){
for( R i = 1 ; i <= m ; i ++ ) f [1][i] = tr( 1 , m ) ;
for( R i = 2 ; i <= k ; i ++ ){
for( R j = 1 ; j <= m ; j ++ ){
int ans = 0 ;
for( R l = 1 ; l < j ; l ++ )
ans = J ( ans , f [i - 1][l] ) ;
f [i][j] = J ( ( T( ans , fm ) ) , T( f [i - 1][j] , T( j , fm ) ) ) ;
}
}
for( R i = 1 ; i <= m ; i ++ ) ans = J( ans , T( f [k][i] , w [i] ) ) ;
printf( "%lld\n" , ( ans * day ) % M ) ;
}
signed main(){
sc() ;
work() ;
return 0 ;
}
$The \ light \ has \ betrayed \ me$

浙公网安备 33010602011771号