[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
*/
posted @ 2020-08-05 21:05  Arextre  阅读(237)  评论(0编辑  收藏  举报