题目描述
给你一个数组R,包含N个元素,求有多少满足条件的序列A使得
0 ≤ A[i] ≤ R[i]
A[0]+A[1]+...+A[N-1]=A[0] or A[1]... or A[N-1]
输出答案对1e9+9取模
输入描述:
第一行输入一个整数N (2 ≤ N ≤ 10)
第二行输入N个整数 R[i] (1 ≤ R[i] ≤ 1e18)
输出描述:
输出一个整数
示例1
输入
2
3 5
输出
15
示例2
输入
3
3 3 3
输出
16
示例3
输入
2
1 128
输出
194
示例4
输入
4
26 74 25 30
输出
8409
示例5
输入
2
1000000000 1000000000
输出
420352509
备注:
子任务1: n <= 3 子任务2: n <= 5 子任务3: 无限制
解题思路
这道题目的突破口是由A[0]+A[1]+...+A[N-1]=A[0] or A[1]... or A[N-1]所推出来的条件,即把A[i]全部相加得到的和sum用二进制来表示,sum二进制上的任何一位的1,都只能由数组A[i]中的某一位提供
我们就可以遍历sum二进制的每一位,根据另外一个限制条件0 ≤ A[i] ≤ R[i](这个可以用状态压缩来表示,即一个数的二进制其第i位上的数来表示A[i]是否被限制,0表示限制,1表示不受限制(限制的意思是只有当R[i]的当前位上有1时才可以取1,不受限制就取1和0都可以)),来判断其每一位数字能否取1,以及取1时可能由那个A[i]提供,由此得到序列A的数量
此时分为三种情况,第一种,当前位取0,则当前位取1且受限制的全部变为不受限制
第二种,当前位取1且受限制,则除自己继续保持受限制以外,其他位取1且受限制的全部变为不受限制
第三种,当前位取1且不受限制,则当前位取1且受限制的全部变为不受限制
限制与不受限制:设R0=10011(二进制),如果SUM的第五位上取1且由A0贡献,既A0>=10000,那么当我们第四位取1时,A0由于0 ≤ A[i] ≤ R[i]无法继续贡献,直到枚举至第二位,R0有1时,才能继续贡献,如果之前第5位的时候A0不贡献1,既A0<10000,那么在考虑后面4,3,2,1位的时候,A0可以任意选择贡献1还是不贡献1,这就是两者的区别
用数组dp[i][j]来表示在sum二进制的第i位时,其A[i]的限制状态为j时,序列A可能的数量
核心代码
#include <bits/stdc++.h>
using namespace std;
long long dp[66][1<<12];///dp[i][j]表示在二进制的第i位,且限制状态为j的情况下,序列A的可能数量,0代表受限,1代表不受限
int N;
long long R[11];
typedef long long ll;
const int mod=1e9+9;
int dfs(int a,int b){///探索在二进制的第a位,且限制状态为b的情况下,序列A的可能数量
if(a<0)return 1;///这是从a=0得来
if(dp[a][b]!=0)return dp[a][b];
int ans=0;
for(int i=0;i<N;i++){///把当前位上有1的记录下来
if(((ll)1<<a)&R[i])ans=ans|(1<<i);}
long long sum=0;///分情况讨论
sum+=dfs(a-1,b|ans);///取0
for(int i=0;i<N;i++){
if(((ll)1<<a)&R[i] && !((1<<i)&b))///取1且受限
sum+=dfs(a-1,(b|ans)-(1<<i));
else if((1<<i)&b)///取1不受限
sum+=dfs(a-1,b|ans);
}
dp[a][b]=sum%mod;
return dp[a][b];}
需要注意的细节是,当我们运算1<<a时,由于a可能会比较大,如果不把1设置成long long类型可能会溢出
题解代码
#include <bits/stdc++.h>
using namespace std;
long long dp[66][1<<12];///dp[i][j]表示在二进制的第i位,且限制状态为j的情况下,序列A的可能数量,0代表受限,1代表不受限
int N;
long long R[11];
typedef long long ll;
const int mod=1e9+9;
int dfs(int a,int b){///探索在二进制的第a位,且限制状态为b的情况下,序列A的可能数量
if(a<0)return 1;///这是从a=0得来
if(dp[a][b]!=0)return dp[a][b];
int ans=0;
for(int i=0;i<N;i++){///把当前位上有1的记录下来
if(((ll)1<<a)&R[i])ans=ans|(1<<i);}
long long sum=0;///分情况讨论
sum+=dfs(a-1,b|ans);///取0
for(int i=0;i<N;i++){
if(((ll)1<<a)&R[i] && !((1<<i)&b))///取1且受限
sum+=dfs(a-1,(b|ans)-(1<<i));
else if((1<<i)&b)///取1不受限
sum+=dfs(a-1,b|ans);
}
dp[a][b]=sum%mod;
return dp[a][b];}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
///关闭输入输出流
cin>>N;
for(int i=0;i<N;i++)cin>>R[i];
dfs(63,0);///sum最长有64位二进制
cout<<dp[63][0]<<endl;}