题目分析:每次写和组合数有关的题目都要纠结半天= =||。
由于每次翻转的张数x是确定的,那么每次翻转的奇偶性也是确定的。如果x为奇数,则这次翻转后1的个数的增量一定是奇数(增量可以是负数);同理x为偶数,则这次翻转后1的个数的增量一定是偶数。并且如果x的和为奇数,则最后的正面朝上的1的个数也是奇数个,和为偶数同理。
如果我们得到了最后一次翻转后能达到的最小的正面朝上的1的个数以及最大的个数,那么易得L,L+2,L+4,...,R-2,R,一定都是可以达到的(可以理解为既然能达到那么大(那么小),那么我总可以使它不那么大(那么小)),而且L,R一定是同奇偶的。
那么最大的L和最小的R通过递推可以计算出来,之后就是计算组合数了(x个1随意摆放的种数)。由于本题的数据符合费马小定理,那么可以用费马小定理将组合数中的除法转化成乘法。
假设用C(n,m)表示从n个位置选择m个放1的方案数。那么C(n,m) = n!/(m!*(n-m)!),设(m!*(n-m)!) = a,由费马小定理可知:a^(p-2) = (1/a)%p。所以将除法转化为乘法以后套套快速幂即可。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
#define REP( i , a , b ) for ( int i = a ; i < b ; ++ i )
#define REV( i , a , b ) for ( int i = a - 1 ; i >= b ; -- i )
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define FOV( i , a , b ) for ( int i = a ; i >= b ; -- i )
#define CLR( a , x ) memset ( a , x , sizeof a )
typedef long long LL ;
const int mod = 1e9 + 9 ;
const int MAXN = 100005 ;
LL f[MAXN] ;
int n , m ;
void fun () {
f[0] = 1 ;
REP ( i , 1 , MAXN )
f[i] = ( f[i - 1] * i ) % mod ;
}
LL pow ( LL a , int b ) {
LL res = 1 , tmp = a ;
while ( b ) {
if ( b & 1 )
res = res * tmp % mod ;
tmp = tmp * tmp % mod ;
b >>= 1 ;
}
return res ;
}
void solve () {
int L = 0 , R = 0 ;
int l , r , x ;
REP ( i , 0 , n ) {
scanf ( "%d" , &x ) ;
if ( x <= L )
l = L - x ;
else if ( x <= R )
l = ( L ^ x ) & 1 ;
else
l = x - R ;
if ( x + R <= m )
r = x + R ;
else if ( x + L <= m )
r = m - ( ( ( L + x ) ^ m ) & 1 ) ;
else
r = m - ( x + L - m ) ;
L = l , R = r ;
}
LL ans = 0 ;
for ( int i = L ; i <= R ; i += 2 )
ans = ( ans + ( f[m] % mod ) * pow ( f[i] * f[m - i] % mod , mod - 2 ) % mod ) % mod ;
printf ( "%I64d\n" , ans ) ;
}
int main () {
fun () ;
while ( ~scanf ( "%d%d" , &n , &m ) )
solve () ;
return 0 ;
}