ZR24NOIP1B. 数数

ZR24NOIP1B. 数数

给你一个长度为 \(n\le 1600\) 的二进制数,其中某些位未知,是 ?。问 ? 的所有取值得到的 \(x\)\([0,x-1]\) 中不含长度为 \(k\le 20\) 的回文串的数字(含前导 \(0\))的个数的和。

首先显然是数位 DP。

考虑从高位枚举到低位,假设没有 ?,状态记位数 \((1600)\) 和是否顶到上界 \((0/1)\)。枚举第 \(i\) 位填什么数字,需要判断填上之后会不会和前面 \(k-1\) 位形成一个回文串,因此我们还要记录前 \(k-1\) 位是什么 \((2^{19})\)。有问号的话转移的时候再枚举一下 ? 填什么就好了。然后判断回文串,但是显然这样过不了。我们直接预处理所有可能的回文串 \((2^{10})\),然后把他们丢到 AC 自动机里,状态改记 AC 自动机节点编号,转移的时候跳一下指针。容易发现因为回文串左右两边是一样的,所以回文串个数只有 \(2^{10}\)

复杂度:AC 自动机预处理时间和空间都是 \(2^{\frac{k}{2}}k\),DP 转移状态是 \(n2^{\frac{k}{2}}k\) 的,可以通过。

#include<bits/stdc++.h>
// #define LOCAL
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x++)
using namespace std;
typedef long long ll;
const int N=2e3,mod=1e9+7;
struct acam{
	int son[N*21][2],sum[N*21];
	int fail[N*21]={0,1};
	int cnt=1;
	void insert(int x,int len) {
		int p=1;
		rep(i,0,len-1) {
			int op=x&1;
			if(!son[p][op]) son[p][op]=++cnt;
			p=son[p][op];
            x>>=1;
		}
		sum[p]++;
	}
	void build(){
		queue<int> q;
		rep(i,0,1) {
            if(son[1][i]) q.push(son[1][i]),fail[son[1][i]]=1;
            else son[1][i]=1;
        }
		while(!q.empty()) {
			int u=q.front();
			q.pop();
			rep(i,0,1) {
				int v=son[u][i];
				if(v) fail[v]=son[fail[u]][i],q.push(v);
				else son[u][i]=son[fail[u]][i];
			}
		}
	}
}T;
int n,k;
char s[N];
ll dp[2][N*21][2];
int re(int x){
    if(k&1) x>>=1;
    int y=0;
    rep(i,1,k/2){
        y<<=1;
        y+=x&1;
        x>>=1;
    }
    return y;
}
void add(ll &a,ll b) {a=a+b>=mod?a+b-mod:a+b;}
void solve(int i,int x) {
    rep(l1,0,1) 
        rep(p,1,T.cnt) {
            if(dp[i^1][p][l1]==0) continue;
            rep(a,0,1) {
                if(a>x&&l1) continue;
                int l=l1&a==x;
                int ps=T.son[p][a];
                if(T.sum[ps]) continue;
                add(dp[i][ps][l],dp[i^1][p][l1]);
            }
        }
}
ll ans;
int main(){
	#ifdef LOCAL
	freopen("in.txt","r",stdin);
	freopen("my.out","w",stdout);
	#endif
    sf("%d%d",&n,&k);
    sf("%s",s+1);
    rep(i,0,(1<<((k+1)/2))-1) {
        int x=(i<<(k/2))+re(i);
        // pf("%d ",x);
        T.insert(x,k);
    }
    pf("\n");
    T.build();
    dp[0][1][1]=1;
    rep(i,1,n) {
        memset(dp[i&1],0,sizeof(dp[i&1]));
        if(s[i]=='0') solve(i&1,0);
        else if(s[i]=='1') solve(i&1,1);
        else solve(i&1,0),solve(i&1,1); 
    }
    rep(i,1,T.cnt){
        add(ans,dp[n&1][i][0]);
    }
    pf("%lld\n",ans);
}
posted @ 2024-09-15 16:07  liyixin  阅读(5)  评论(0编辑  收藏  举报