[CF757D]Felicity's Big Secret Revealed
题目
题解
有个极为显然的想法:定义 \(f[i][j]\) 为最后一下划分到 \(i\) 之前的位置,前面的划分使得出现方案为 \(j\) 的合法方案数。
但是 \(j\) 应该开多大?我们可以计算一下:设 \(f(x)\) 为 \(x\) 的二进制长度,那么有
\[\sum_{i=1}^{21}f(i)=78
\]
显然,划分出来的数组中,最大的数不能超过 \(20\),不然显然无解
那么 \(j\le 2^{20}\),这个范围我们确定,而转移也很明显,枚举下一个划分点 \(k\),那么有
\[f[k+1][j']=\sum f[i][j]
\]
其中有 \([i,k]\) 这个区间的划分能使集合 \(j\) 变为 \(j'\),至于为什么第一维是 \(k+1\),是因为我们的定义是划分到 \(k+1\) 之前的位置 \(k\),故而 \(k+1\).
对于初始化,我们第一个划分的区间是要被舍去的,所以有 \(f[i][0]\) 表示在 \(i\) 位之前有一个划分,其出现方案为空集,我们将其强制为 \(1\) 表示有这样一种划分方案(但是第一个区间被舍去,便没有出现的数字)
最后,有效划分为全部数字都出现过,即 \(j\) 的所有元素均出现过,相当于其二进制表示下,所有位数均为 \(1\),我们将这样的方案累加便可得到最终结果。
代码
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
#define rep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i<=i##_end_;++i)
#define fep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i>=i##_end_;--i)
#define erep(i,u) for(signed i=tail[u],v=e[i].to;i;i=e[i].nxt,v=e[i].to)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
typedef long long LL;
// typedef pair<int,int> pii;
typedef unsigned long long ull;
typedef unsigned uint;
#define Endl putchar('\n')
// #define int long long
// #define int unsigned
// #define int unsigned long long
#define cg (c=getchar())
template<class T>inline void read(T& x){
char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
if(f)x=-x;
}
template<class T>inline T read(const T sample){
T x=0;char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
return f?-x:x;
}
template<class T>void fwrit(const T x){//just short,int and long long
if(x<0)return (void)(putchar('-'),fwrit(-x));
if(x>9)fwrit(x/10);
putchar(x%10^48);
}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
}
const int MOD=1e9+7;
const int MAXN=75;
const int MAXSIZE=1<<20;
int a[MAXN+5],n;
int num[MAXN+5][MAXN+5];
inline void Init(){
n=read(1);
rep(i,1,n)scanf("%1d",&a[i]);
}
inline void Get_num(){
int tmp;
rep(i,1,n){tmp=0;
for(int j=i;j<=n;++j){
(tmp<<=1)|=a[j];
if(tmp>20)break;//maxx<=20
num[i][j]=tmp;
// printf("[%d, %d] == %d\n",i,j,num[i][j]);
}
}
}
inline void Plus(int& a,const int delta){
a+=delta;
if(a>=MOD)a-=MOD;
}
int f[MAXN+5][MAXSIZE+5];
inline void Get_f(){
int all=(1<<20)-1;
rep(i,1,n){
f[i][0]=1;
rep(j,0,all)if(f[i][j]){
rep(k,i,n)if(num[i][k])
Plus(f[k+1][j|(1<<(num[i][k]-1))],f[i][j]);
}
}int ans=0;
rep(i,1,n+1)rep(j,1,20)
Plus(ans,f[i][(1<<j)-1]);
writc(ans,'\n');
}
signed main(){
Init();
Get_num();
Get_f();
return 0;
}
/*
设 f(x) 为 x 的二进制长度, 则有
\sum_{1<=i<=21}f(i)=78
那么 maxx<=20, 而 f(20)=5
*/