BZOJ CF388D. Fox and Perfect Sets [线性基 数位DP]

时间:2023-03-09 08:09:03
BZOJ CF388D. Fox and Perfect Sets [线性基 数位DP]

CF388D. Fox and Perfect Sets

题意:求最大元素\(le n\)的线性空间的个数


给神题跪了 orz

容易想到 每个线性基对应唯一的线性空间,我们可以统计满足条件的对应空间不同的线性基个数

每一位我们插入一个向量,就获得了这一位的控制权,否则这一位是*的

因为要\(le n\),可以使用数位DP

从高位到低位考虑,设当前第i位,已经插入了j个向量

没有天际线的限制

  • 插入向量i的话,之前的向量位i必须是0,1种情况
  • 不插入向量i的话,之前的向量位i可以任选,\(2^j\)种情况

考虑天际线的限制

  • 不插入向量i,有\(2^{j-1}\)种情况可以继续顶着天际线
  • 如果a[i]==1,还有\(2^{j-1}\)种情况可以小于天际线
  • a[i]==1时可以插入向量i

然后我的转移方程写的好丑啊....然后鏼鏼鏼了一个简短的写法

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long ll;
#define pii pair<int, int>
#define fir first
#define sec second
const int N=40, P=1e9+7;
inline ll read() {
char c=getchar(); ll x=0, f=1;
while(c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}
while(c>='0' && c<='9') {x=x*10+c-'0'; c=getchar();}
return x*f;
} int n, f[N][N][2], a[N], len;
ll mi[N];
inline void mod(int &x) {if(x>=P) x-=P;}
int dfs(int d, int j, int sky) { //printf("dfs %d %d %d\n",d,j,sky);
if(d==0) return 1;
if(f[d][j][sky] != -1) return f[d][j][sky];
int &now = f[d][j][sky], lim = sky ? a[d] : 1;
now=0;
if(!sky) {
mod(now += mi[j] * dfs(d-1, j, 0) %P );
mod(now += dfs(d-1, j+1, 0)%P );
} else {
mod(now += (j==0 ? 1 : mi[j-1]) * dfs(d-1, j, sky && 0==lim) %P);
if(a[d]==1) mod(now += dfs(d-1, j+1, sky && 1==lim)%P ),
mod(now += (j==0 ? 0 : mi[j-1]) * dfs(d-1, j, sky && 1==lim) %P );
}
//for(int i=0; i<=lim; i++) {
// mod(now += (j==0 ? !i : mi[j-1]) * dfs(d-1, j, sky && i==lim) %P);
// if(i==1) mod(now += dfs(d-1, j+1, sky && i==lim));
//} return now;
}
int main() {
freopen("in","r",stdin);
mi[0]=1;
for(int i=1; i<=30; i++) mi[i] = (mi[i-1]<<1)%P;
n=read();
while(n) a[++len]=n&1, n>>=1;
memset(f, -1, sizeof(f));
printf("%d", dfs(len, 0, 1));
}