题目描述
给你一个数组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<=10 ; A[i]<=1E18
记忆化搜索好题
分析 :题目要求 A1+A2+A3…+AN=A1|A2|A3…|AN ,把|运算符用+号来表示,A1|A2=A1+A2-2*(A1&A2),可以推出,把A1-AN转化成二进制表达后,同一位二进制位上不能有两个以上的1,不然一定会出现式子里的减法从而小于A1+A2…+AN,我们不妨设SUM为所有A数组的和,然后把SUM转化成二进制,条件就被我们转化成,SUM二进制上的 1 只会来源于N个Ai中的一个,根据这个设计DP的状态表示。
状态表示:DP[iI[j](i=62,j=1<<12)表示枚举到sum二进制的第i位时,R数组里各个数字的限制情况,这里特别解释下限制的意思,因为我们枚举SUM二进制时要考虑二进制上1的来源,设R1=10010(二进制),如果SUM的第五位是由A1的数字贡献的,既A1>=10000,那么当我们继续考虑第四位的1时,A1必然无法继续作出贡献,会导致A1超出R1的限制,直到枚举至第二位,它本身上界有1时,才能继续贡献,可是如果之前第5位的时候A1不贡献1,那么根据二进制,高位的1大于所有比它低位的1的和(10000>01111),既A1<10000,那么在考虑后面4,3,2,1位的时候,A1可以任意选择贡献1还是不贡献1,这就是限制与不限制的区别。至此J的含义已经清楚 J可以被当成一个12位的二进制数,如果第一位上是1则代表,A1是不受限制的,反之则受限,受限的Ai只能在其上届Ri有1的时候才能作出贡献。
转移方程:假设枚举到第I位,限制状态是J,如果SUM该位二进制数取0,则在该位上是1的R[i],在之后的搜索中全部变成无限制,而如果取1,1的来源有两种情况,一种是R[I]被限制了,但这一位上有1,那么R[I]依旧保持限制,其他这一位上有1的变为无限制,第二种是R[I]没被限制,那么R[I]保持无限制,其他这一位上有1的也为无限制。当然之前就已经无限制的数字在之后依旧无限制,总计三种情况,记忆化搜索。
//喜欢的话可以帮忙去CSDN加个点击)https://blog.csdn.net/NODOGNOFAIL/article/details/109720902
初始所有的数字都是被限制的。
#include <stdio.h> #include <cstring> #include<stack> #include<deque> #include<cstdio> #include<cmath> #include<ctime> #include<algorithm> #include <iostream> #include<bitset> #include <queue> #include<set> #include <map> #include<unordered_map> #define For(i, R, y) for(int i=R;i<=y;i++) #define _For(i, R, y) for(int i=R;i>=y;i--) #define Mem(f,R) memset(f,R,sizeof(f)) #define Sca(R) scanf("%d", &R) #define Sca2(R,y) scanf("%d%d",&R,&y) #define Sca3(R,y,z) scanf("%d%d%d",&R,&y,&z) #define Scl(R) scanf("%lld",&R) #define Pri(R) printf("%d\n",4 R) #define Prl(R) printf("%lld\n",R) #define LL long long #define ULL unsigned long long using namespace std; const int INF = 0x3f3f3f3f; const int mod=1e9+9; int dp[63][1<<12]; long long R[12]; int n; int dfs(int p,int limit){ //cout<<p<<" "<<limit<<endl; if(p<0) return 1; if(dp[p][limit]!=-1) return dp[p][limit]; int ans=0;//用来记录第P位上有1的R[i],用于后续的限制解除. for(int i=1;i<=n;i++) if(R[i]&((long long)1<<p)) ans|=(1<<i); long long res=0; res+=dfs(p-1,ans|limit);//如果第P位取0,所有ans记录的Ri全部解除限制ans|limit。 for(int i=1;i<=n;i++){ if(((long long)1<<i)&limit)//取1的时候,如果Ri已经无限制了,保持无限制,其他这一位上有1的也为无限制。当然之前就已经无限制的数字在之后依旧无限制 res+=dfs(p-1,ans|limit); else if(!((1<<i)&limit)&&ans&(1<<i))//R[I]被限制了,但这一位上有1,那么R[I]依旧保持限制,其他这一位上有1的变为无限制 res+=dfs(p-1,(ans^(1<<i))|(limit)); } dp[p][limit]=res%mod; return dp[p][limit]; } int main(){ memset(dp,-1,sizeof(dp)); cin>>n; for(int i=1;i<=n;i++){ scanf("%lld",&R[i]); } dfs(61,0); cout<<dp[61][0]<<endl; return 0; }