题意:
给你N次操作,M张扑克,给你N个数A1,A2,..AN,其中Ai代表你i次操作能翻任意Ai张牌,求N次扑克牌操作后有多少种情况
想法: 我当时一直在做这题,当时没想到可以用区间去维护最后可能出现的1的个数。。 只是想到把前一个操作可能出现的1的个数往后一个操作递推。。。 但是酱紫时间复杂度N^2。。
方法: 用一个区间[min,max] 去维护可能出现1的个数的情况, 设前一个操作翻A张牌,后一个操作翻B张牌,两次操作都翻了的牌的数目为C, 则两个操作之后可能出现的1的个数为A+B-2*C。。 可得出现1的个数要不全为奇数,要不全为偶数,所以用区间[min,max]中的所有奇数(偶数)去表示1出现的个数即可
求出1可能出现的个数之后,设min,min+2,min+4.... max为1可能出现的个数,则ans=(C(m, min)+ C(m, min+2) +..+ C(m,max) )%MOD;
计算(A/B)%MOD = (A*(B的逆元))%MOD时, 由费马小定理可得 当MOD为素数时,B的逆元为B^(MOD-2)%MOD
所以这题我们用二分快速幂可以算出结果
PS: 妈蛋啊 这题我取余又取错了。。。。 这已经不是第一次了。。坑爹啊。。
代码:
#include <cstdio>
#include <cstring>
#define LL long long
#define maxn 100005
#define MOD 1000000009
LL C[maxn];
int Fabs(int x)
{
return x > 0? x: -x;
}
LL erfen(int a, int b)
{
LL ans= 1;
LL s= a;
while(b)
{
if(b&1)
ans= (ans * s)%MOD;
s= (s * s)%MOD;
b/= 2;
}
return ans;
}
int main()
{
int n, m;
while(scanf("%d %d",&n,&m)!=EOF)
{
int min= 0;
int max= 0;
for(int i= 1; i<= n; i++)
{
int num;
scanf("%d",&num);
int _max= max;
int _min= min;
//max 更新
if(_max + num <= m)
max= _max+ num;
else if(_min+ num<= m)
{
int xx= m- num;
if(xx%2 == _max%2) // 如果xx与_max奇偶性相同,代表xx可取
max= xx+ num;
else // 否则取x-1
max= xx+ num - 1;
}
else if(_min+num > m)
max= 2*m- (_min+ num);
//min的更新
if(num>= _max)
min= num- _max;
else if(num>= _min)
min= num%2== _max%2? 0: 1;
else if(num< _min)
min= _min- num;
}
LL up= 1;
LL down= 1;
LL ans= 0;
if(min== 0)
ans++;
for(int i= 1,j= m; i<= max; i++,j--)
{
up= (up * j)%MOD;
down= (down * i)%MOD;
if(i>= min && ( i- min )%2==0)
ans= ( ans + (up * erfen(down,MOD-2) )%MOD ) %MOD;
}
printf("%lld\n",ans);
}
return 0;
}