Comet OJ - Contest #3子序列子序列子序列...

Comet OJ - Contest #3子序列子序列子序列…Comet OJ - Contest #3子序列子序列子序列...

#include<bits/stdc++.h>

#define per(i,a,b) for(int i = (a);i <= (b);++i) 
#define rep(i,a,b) for(int i = (a);i >= (b);--i)
using namespace std;
const int maxn = 5e3;
const int mod = (1e9 + 7);
int n = 0,m = 0;
int a[maxn+10],dp[2][14][maxn+10],f[2][maxn+10];
int cal(int& x){
	int res = 0;
	while(x % 2 == 0){
		++res;
		x >>= 1; 
	}
	return res;
}
inline void upd(int& x){
	if(x >= mod){
		x -= mod;	
	}
}
void solve(){ 
	int nf = 0,tm = m,em = m;
	int c = cal(em);
	
	dp[nf][0][0] = 1;
	per(i,1,n){
		nf ^= 1;
		tm = m;
		dp[nf][0][0] = 0;
		//per(j,1,c){//初始化 
		for(int j = 1;j <= c && j <= i;++j){
			per(k,0,tm-1){
				dp[nf][j][k] = 0;
			}
		}
		dp[nf][0][0] += dp[nf^1][0][0];	dp[nf][1][a[i]] += dp[nf^1][0][0];
		tm = m;
		for(int j = 1;j <= c && j <= i-1;++j){//两个灯饰条件 
			per(k,0,tm-1){
				dp[nf][j][k] += dp[nf^1][j][k];	upd(dp[nf][j][k]);
				dp[nf][j+1][(k+a[i]) % (tm>>1)] += dp[nf^1][j][k]; upd(dp[nf][j+1][(k + a[i]) % (tm>>1)]);
				
			}
			tm >>= 1;
		}
	}
	int ans = 0;
	tm = m;
	per(j,1,c){//减去那些可以整除em的,但是比m小的方案 
		for(int k = em;k < tm;k += em){//不可以取等号,<表示上面那种情况 
			ans += (mod - dp[nf][j][k]); upd(ans);	
		}
		tm >>= 1;//tm /2来控制2次方个数 
	}
	f[0][0] = 1;
	nf = 0;
	per(i,1,n){//一起算%em==0的方案,由于前面减去了 些可以整除em的,但是比m小的方案 
		nf ^= 1; //所以最后就是结果 
		per(j,0,em-1){
			f[nf][j] = (f[nf^1][j] + f[nf^1][(j-a[i] + m) % em]); upd(f[nf][j]);
		}
	}
	f[nf][0] += (mod - 1); upd(f[nf][0]);//减去f[0][0] 
	ans += (f[nf][0]); upd(ans);
	ans = ((ans % mod) + mod) % mod;
	printf("%d\n",ans);
}
int main(){
	scanf("%d %d",&n,&m);
	per(i,1,n){
		scanf("%d",a+i);
	}
	solve();
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值