ICPC2020 济南站 L 题 (数位dp)

题目链接:

发现 (m) 很小,所以只需要先数位 (dp),然后暴力枚举后 (8) 位的情况即可,因为涉及到进位,所以还需要维护第八位之前有几个连续的 (1)
(dp[0/1][i][0/1][0/1]) 表示,当前到第 (i) 位,卡不卡上界,从第 (i) 位向前有多少个连续的 (1)(奇偶), 有多少个 (1) (奇偶)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn = 110;

int T, m, lev; ll L;
int a[maxn], c[maxn], d[maxn], cnt = 0;
int su[600]; 
ll A1[2][65],A2[2][65];

ll dp[2][65][2][2];

ll dfs(int pos, int sum, int in, int lim){
	if(dp[lim][pos][sum & 1][in & 1] != -1) return dp[lim][pos][sum & 1][in & 1];
	if(pos >= cnt + 1 - 8){
		ll res = 0;
		if(lim) res += A1[sum & 1][in & 1];
		else res += A2[sum & 1][in & 1];
		dp[lim][pos][sum & 1][in & 1] = res;
		return res;
	}

	ll res = 0;
	int li = (lim) ? (c[pos]) : 1;
	for(int i = 0 ; i <= li ; ++i){
		if(i == 0) res += dfs(pos + 1, sum + i, 0, lim & (i == li));
		else res += dfs(pos + 1, sum + i, in + i, lim & (i == li));
	}
	dp[lim][pos][sum & 1][in & 1] = res;
	return res; 
}

ll calc(ll x){
	cnt = 0;
	ll tmp = x;
	while(tmp){
		c[++cnt] = tmp % 2ll;
		tmp /= 2ll;
	}
	
	for(int i = 1 ; i <= cnt / 2 ; ++i) swap(c[i], c[cnt - i + 1]);
		for(int sum=0;sum<=1;++sum){
			for(int in = 0;in <= 1;++in ){
				for(int i = 0 ; i <= lev ; ++i){
					int tmp=1;
					for(int j = 0 ; j < m ; ++j){			
						if(i+j>=256){
							tmp &= (((sum + su[i+j] - in)%2+2)%2== a[j]);
						}else{
							tmp &= (((sum + su[i+j]) % 2+2)%2 == a[j]);
						}
					}
					A1[sum][in]+=tmp; 
				}
				for(int i = 0 ; i < 256 ; ++i){
					int tmp=1;
					for(int j = 0 ; j < m ; ++j){			
						if(i+j>=256){
							tmp &= (((sum + su[i+j] - in)%2+2)%2== a[j]);
						}else{
							tmp &= (((sum + su[i+j]) % 2+2)%2 == a[j]);
						}
					}
					A2[sum][in]+=tmp; 
				}
			}
		}
	return dfs(1, 0, 0, 1);
}

int main(){
	scanf("%d", &T);
	for(int i = 0 ; i < 512 ; ++i){
		int tmp = i;
		while(tmp){
			su[i] += (tmp & 1);
			tmp >>= 1;
		}
	}
	while(T--){
		memset(dp, -1, sizeof(dp));
		memset(A1,0,sizeof(A1)); 
		memset(A2,0,sizeof(A2));
		scanf("%d%lld", &m, &L);
		lev = L % (1 << 8);
		for(int i = 0 ; i < m ; ++i) scanf("%d", &a[i]);
		
		printf("%lld
", calc(L));
	}
	return 0;
}
原文地址:https://www.cnblogs.com/tuchen/p/14204100.html