这种题目数据范围都非常大,一般不是dp就是组合数
然后可以看到,翻牌并不要求一定要是连续的,所以可以任意翻,那么必然在奇偶性上就可以做文章
求出翻完所有后,1的个数为最小为L,最大为R,且L和R的奇偶性相同。
那么答案就是for(int i=L;i<=R;i+=2) ans+=C(n,i),ans%=mod;
因为最多能有R个1,如果在中间操作时,2个操作我可以一个选择0,一个选择1,换句话说,偶数步操作可以忽略一部分
所以[L,R]都是可以取到的,,那么问题是,如何求出L和R
贪心思路:
求L时使1尽量变成0,求R时使0尽量变成1
求L的时候,不仅要维护上一次的L,还要维护上一次的R
设某次操作要翻k张牌
如果L>=t 说明上一次中,1最少也有L个,所以这回1最少有L-t个
否则,L<t
-|如果t<=R 说明上一次中,最大能达到的1比t还要多,那么我就能找到一个最接近>=t的值,把剩下的t个1变成0,也就是(R-t)%2
-|如果t>R 说明就算我消耗完所有的1,还会有剩余操作,所以剩下t-R
求R的过程类似
求组合数C就直接套模板,用快速幂+费马小定理就可以了
#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<functional>
#include<algorithm>
using namespace std;
typedef long long LL;
const int MX = 100000 + 5;
const int INF = 0x3f3f3f3f;
const LL mod = 1000000009;
LL A[MX], invA[MX];
LL power(LL a, LL b) {
LL ret = 1;
while(b) {
if(b & 1) ret = ret * a % mod;
a = a * a % mod;
b >>= 1;
}
return ret;
}
void init() {
A[0] = 1;
for(int i = 1; i < MX; i++) {
A[i] = (A[i - 1] * i) % mod;
}
invA[MX - 1] = power(A[MX - 1], mod - 2);
for(int i = MX - 2; i >= 0; i--) {
invA[i] = invA[i + 1] * (i + 1) % mod;
}
}
LL C(int n, int m) {
if(n < 0 || m < 0 || m > n) return 0;
if(m == 0 || m == n) return 1;
return A[n] * invA[n - m] % mod * invA[m] % mod;
}
int main() {
int n, m;
init();
while(~scanf("%d%d", &m, &n)) {
int L = 0, R = 0, Lt, Rt, t;
for(int i = 1; i <= m; i++) {
scanf("%d", &t);
if(L >= t) Lt = L - t;
else if(t <= R) Lt = (R - t) % 2;
else Lt = t - R;
if(n - R >= t) Rt = R + t;
else if(t <= n - L) Rt = n - (n - L - t) % 2;
else Rt = 2 * n - t - L;
L = Lt;
R = Rt;
}
LL ans = 0;
for(int i = L; i <= R; i += 2) {
ans += C(n, i);
ans %= mod;
}
printf("%I64d\n", ans);
}
return 0;
}