题目大意
定义一个集合是完美的,当且仅当
∀a,b∈S,a⊕b∈S
给出k,求出最大的数不超过k的完美集合个数
解法
显然一个完美集合可以由一组线性基表示:
对于集合S有线性基:
α1,α2⋯αk
考虑如何唯一表示一个集合(即如何唯一表示一组线性基)
首先假定一组线性基,符合:
∀1≤i<k,αi>αi+1
对于一个线性基
αi
,我们希望它极大,即通过与
αj(j>i)
的运算使得它最大。
如下的线性基不是极大的:
α1=1α2=0α3=0010101
因为 α1 还可以与 α2 抑或得到111
上述线性基对应的极大线性基是:
α1=1α2=0α3=0110111
那么就可以DP了,设f[i][j]表示当前做完了二进制下的i位以及高位,现在的线性基有j个的方案数。
如果在第i位不加入一个新的线性基,那么这一位上就可以随便选
如果在第i位加入一个新的线性基,那么这一位上的数都必须为1
时间复杂度 O(n2)
代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<set>
#include<bitset>
#include<map>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long LL;
typedef double db;
int get(){
char ch;
while(ch=getchar(),(ch<'0'||ch>'9')&&ch!='-');
if (ch=='-'){
int s=0;
while(ch=getchar(),ch>='0'&&ch<='9')s=s*10+ch-'0';
return -s;
}
int s=ch-'0';
while(ch=getchar(),ch>='0'&&ch<='9')s=s*10+ch-'0';
return s;
}
const int mo = 1e+9+7;
const int N = 40;
LL f[N][N][2];
int n;
int a[N],w;
int main(){
int x=get();
while(x){
a[++n]=x%2;
x/=2;
}
f[n][1][1]=1;
fd(i,n-1,1){
fo(j,1,n){
//0
f[i][j][0]=(f[i][j][0]+f[i+1][j][0]*(1<<(j-1))%mo)%mo;
if (a[i])f[i][j][0]=(f[i][j][0]+f[i+1][j][1]*(1<<(j-1))%mo)%mo;
else f[i][j][1]=(f[i][j][1]+f[i+1][j][1]*(1<<(j-1))%mo)%mo;
//1
f[i][j][0]=(f[i][j][0]+f[i+1][j][0]*(1<<(j-1))%mo)%mo;
if (a[i])f[i][j][1]=(f[i][j][1]+f[i+1][j][1]*(1<<(j-1))%mo)%mo;
//new
f[i][j+1][0]=(f[i][j+1][0]+f[i+1][j][0])%mo;
if (a[i])f[i][j+1][1]=(f[i][j+1][1]+f[i+1][j][1])%mo;
}
f[i][1][0]=(f[i][1][0]+1)%mo;
}
LL ans=1;
fo(i,1,n)ans=(ans+f[1][i][0]+f[1][i][1])%mo;
printf("%I64d\n",ans);
return 0;
}