题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4869
题目大意:
有N张牌,一开始全是盖着的。M次操作,每次可以从中任意选择翻 xi 张牌。问最后的状态有多少种。
解题思路:
最终的结果一定是连续出现的,只需要求出最终的区间。
因为如果对同一张牌进行两次操作,牌的状态不改变。故牌的翻转次数一定是减少偶数次。如果所有数的和是奇数,那么最终结果也一定是奇数。同理,偶数也是一样的。所以只要递推求出最后的区间,计算sum(C(xi,m)(i=0,1,2。。。)),m是总牌数,xi是在区间内连续的奇数或偶数,在模10^9+9就是最终的答案。
代码:
const int mod = 1000000009; using namespace std; typedef long long ll; long long quickpow(long long a, long long b) { if (b < 0) return 0; long long ret = 1; a %= mod; while(b) { if (b & 1) ret = (ret * a) % mod; b >>= 1; a = (a * a) % mod; } return ret; } //费马小定理求逆元(M必须是质数) long long INV(long long a) { return quickpow(a, mod - 2); } ll fac[100010]; int main () { int n, m; int x, l, r; fac[0] = 1; for (int i = 1; i <= 100000; i++) fac[i] = (fac[i - 1] * i) % mod; while(scanf("%d%d", &n, &m) != EOF) { l = 0, r = 1; int nl, nr, k = 0; for(int i = 0; i < n; ++i) { scanf("%d", &x); nl = min(abs(l - x), abs(r - x)); if (l <= x && x <= r) nl = 0; nr = max(m - abs(l + x - m), m - abs(r + x - m)); if (l + x <= m && m <= r + x) nr = m; l = nl, r = nr, k = (k + x) % 2; } ll ans = 0; ll tmp; for (int i = l; i <= r; i++) if (i % 2 == k) { tmp = fac[m] * INV(fac[m - i] * fac[i] % mod) % mod; ans += tmp; ans %= mod; } printf("%I64d\n", ans); } return 0; }