题解
卢卡斯定理:
(nm)≡(⌊np⌋⌊mp⌋)(n%pm%p)(modp)
(
n
m
)
≡
(
⌊
n
p
⌋
⌊
m
p
⌋
)
(
n
%
p
m
%
p
)
(
mod
p
)
很好证明的。
如何保证
(nm) mod 2 =1
(
n
m
)
m
o
d
2
=
1
呢?
结合卢卡斯定理可以发现:
当n&m=m时,上式成立。
我们可以这样暴力枚举:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<iostream>
#include<ctime>
#include<cstdlib>
using namespace std;
const int N=2020,mod=1e9+7;
int n,a[N],dp[N],sum;
inline int rd()
{
char ch=getchar();int x=0,f=1;
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
return x*f;
}
int main(){
int i,j;
n=rd();
if(n>2017){return 0;}
for(i=1;i<=n;++i) a[i]=rd(),dp[i]=1;
for(i=1;i<=n;++i){
for(j=1;j<i;++j){
if((a[i]&a[j]) == a[i])
(dp[i]+=dp[j])%=mod;
}
(sum+=dp[i]-1)%=mod;
}
printf("%d\n",sum);
}
我们就可以得70分了!
还需要优化一下:
我们可以枚举每个数的子集,看枚举到的数是否存在且下标小于当前数的下标,然后加上就好了。
但是对于下标保证单增就很妙了,
我们可以边读入边枚举当前数,加给后面的,这样就不用考虑下标问题了,因为若后面的数枚举到了前面的数,但答案在前面已经加过了这个数,所以即使更新了前面的数的dp数组,也不会对答案产生影响了。
AC代码:
#include<cstdio>
using namespace std;
const int N=233333+10,mod=1e9+7;
int n,pos[N],sum,now,f[N];
inline void mo(int &x){x-= x>=mod? mod:0;}
int main(){
int i,j;
scanf("%d",&n);
for(i=1;i<=n;++i){
scanf("%d",&now);f[now]++;
for(j=(now-1)&now;j;j=(j-1)&now)
mo(f[j]+=f[now]);
mo(sum+=f[now]-1);
}
printf("%d\n",sum);
}