牛客 P21336 和与或 (数位dp)

大意: 给定数组$R$, 求有多少个数组$A$, 满足$0le A_i le R_i$且$A_0+...+A_{N-1}=A_0space or ...space or space A_{N-1}$.

题目条件等价于对于任意的$i,j$, 有$A_i and A_j = 0$, 所以每个二进制位上为$1$的最多只有一个数, 然后跑一次二进制数位$dp$即可.

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <string.h>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
using namespace std;
typedef long long ll;
const int P = 1e9+9;

int n, dp[70][2000];
ll r[20];

//dp[x][y] 表示从二进制最高位填到第d位时, 无限制的a[i]的状态为now的方案数.
int dfs(int d, int now) {
	if (d<0) return 1;
	int &ans = dp[d][now];
	if (~ans) return ans;
	int nxt = now;
	REP(i,0,n-1) if (r[i]>>d&1) nxt |= 1<<i;
	//第d位全为0的方案数
	ans = dfs(d-1,nxt);
	REP(i,0,n-1) {
		//a[i]的第d位为1的方案数 
		if (now>>i&1) { 
			//a[i]无限制, 则下一步全无限制
			ans = (ans+dfs(d-1,nxt))%P;
		}
		else if (nxt>>i&1) { 
			//a[i]有限制, 且r[i]的第d位为1, 则下一步a[i]还有限制
			ans = (ans+dfs(d-1,nxt^(1<<i)))%P;
		}
		//a[i]有限制, 且r[i]的第d位为0, 不能填1
	}
	return ans;
}

int main() {
	scanf("%d", &n);
	REP(i,0,n-1) scanf("%lld", r+i);
	memset(dp,-1,sizeof dp);
	printf("%d
", dfs(60,0));
}
原文地址:https://www.cnblogs.com/uid001/p/11033243.html