• CF585F Digits of Number Pi


    题意:

    codeforces链接

    给定长度为 (n) 的数字串 (s) 和长度为 (d) 的不含前导零的数字串 (x,y(x le y))

    存在长度至少为 (leftlfloorfrac{d}{2} ight floor) 的子串是 (s) 的子串的数字串 (t in [x,y]) 的数量。

    (n le 10^3)(d le 50),答案对 (10^9+7) 取模。


    算一道挺难的题,质量也不错,适合练码力。

    怎么表示一个长度为 (frac d2) 的串在s中出现,我们发现s长度并不是很大,1000,我们把s中所有长度大于等于 (frac d2) 的串一起建立一个AC自动机,然后发现只需要把长度等于 (frac d2) 的建AC自动机就好了,因为包含长度大于 (frac d2) 的串也就肯定包含等于 (frac d2) 的串。

    然后把每个串的末尾点和该点fail子树上所有点打上标记,表示如果走到这个点,就能包含一个长度为 (frac d2) 的子串。

    关于 (fail) 树的意义不想在这里讲了qwq,如果不了解的话可以去康康别的博客。

    AC自动机建好之后,我们将所有的串在这上面跑,能走到打标记的点的串就是可以匹配的串。但是虽然一个匹配串长度只有不到50,但是数量太多了!我们要统计所有属于 ([x,y]) 的数字串,所以我们还得套一个数位dp。

    定义 (f[i][j][0/1][0/1]) 表示枚举到数字第 (i) 位(从高位开始),现在在AC自动机上的哪个位置,是否卡上界,是否已经匹配,的方案数。最后统计答案就是将所有枚举到第 (d) 位的,在任意位置的,卡不卡上界都行的,已经匹配了的,方案数加起来。

    跑两次数位dp记得清空数组,初始状态为 (f[0][0][1][0]=1) ,就是还没有放数字,在根节点,卡上界(一开始肯定要卡的,不然后面放飞了),没有匹配(没有空串肯定没有匹配)。

    转移思路和其他的数位dp一样,这里AC自动机可以方便地进行位置上的转移,代码细节很多,要仔细一点,要么根本没法调。我们将转移按 “数字是否超上界或等于上界”,“是否已经匹配”来分成六类,每类分别进行不同的转移,调试可能也好调一些吧。

    放代码的话我把模数去掉了,不然有点乱。

    #include <bits/stdc++.h>
    #define ll long long
    #define F f[i-1][j]
    using namespace std;
    const int N=101010;
    const int p=1000000007;
    
    int n,m,d;
    int a[53];
    char s[N],z1[53],z2[53];
    int ch[N][10],ed[N],fa[N],cnt,now;
    ll f[53][25678][2][2];
    vector <int> e[N];
    queue <int> q;
    
    void DFS(int u,bool flag) {
    	if(ed[u]) flag = 1;
    	if(flag) ed[u] = 1;
    	for(int i=0;i<e[u].size();i++) DFS(e[u][i],flag);
    }
    
    void AC() {
    	n = strlen(s+1);
    	for(int i=1;i+d/2-1<=n;i++) {
    		now = 0;
    		for(int j=i;j<=i+d/2-1;j++) {
    			int c = s[j] - '0';
    			if(!ch[now][c]) ch[now][c] = ++cnt;
    			now = ch[now][c];
    		}
    		ed[now] = 1;
    	}
    	for(int i=0;i<10;i++) if(ch[0][i]) q.push(ch[0][i]);
    	while(!q.empty()) {
    		int u = q.front(); q.pop();
    		for(int i=0;i<10;i++) {
    			int v = ch[u][i];
    			if(v) {
    				fa[v] = ch[ fa[u] ][i];
    				q.push(v);
    			}
    			else ch[u][i] = ch[ fa[u] ][i];
    		}
    	}
    	for(int i=1;i<=cnt;i++) e[ fa[i] ].push_back(i);
    	DFS(0,0);
    }
    
    int query() {
    	ll ans = 0;
    	memset(f,0,sizeof(f));
    	f[0][0][1][0] = 1;
    	for(int i=1;i<=d;i++) {
    		for(int j=0;j<=cnt;j++) {
    			for(int k=0;k<10;k++) {
    				int v = ch[j][k];
    				if(k<a[i]) {
    					if(ed[v]) {
    						f[i][v][0][1] += F[1][1] + F[0][1] + F[1][0] + F[0][0];
    					}
    					else {
    						f[i][v][0][1] += F[1][1] + F[0][1];
    						f[i][v][0][0] += F[1][0] + F[0][0];
    					}
    				}
    				if(k==a[i]) {
    					if(ed[v]) {
    						f[i][v][1][1] += F[1][1] + F[1][0];
    						f[i][v][0][1] += F[0][1] + F[0][0];
    					}
    					else {
    						f[i][v][1][1] += F[1][1];
    						f[i][v][1][0] += F[1][0];
    						f[i][v][0][1] += F[0][1];
    						f[i][v][0][0] += F[0][0];
    					}
    				}
    				if(k>a[i]) {
    					if(ed[v]) {
    						f[i][v][0][1] += F[0][1] + F[0][0];
    					}
    					else {
    						f[i][v][0][1] += F[0][1];
    						f[i][v][0][0] += F[0][0];
    					}
    				}
    			}
    		}
    	}
    	for(int i=0;i<=cnt;i++) ans += f[d][i][1][1] + f[d][i][0][1];
    	return ans;
    }
    
    int main() {
    	scanf("%s",s+1);
    	scanf("%s%s",z1+1,z2+1); d = strlen(z1+1);
    	AC();
    	for(int i=1;i<=d;i++) a[i] = z1[i] - '0'; int h = d; while(a[h]==0) a[h] = 9, h--; a[h]--;
    		ll ans1 = query();
    	for(int i=1;i<=d;i++) a[i] = z2[i] - '0';
    		ll ans2 = query();
    	cout<<((ans2-ans1)%p+p)%p;
    	return 0;
    }
    

    (2020.5.19) (update)

    傻了傻了,为啥要定义的这么麻烦啊,我们只记录不经过任何标记点的情况数,再用总情况减去它不就行了?

    少了一维,而且状态转移少了好几倍。

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #define QWQ cout<<"QwQ"<<endl;
    #define ll long long
    #include <vector>
    #include <queue>
    #include <stack>
    #include <map>
    #define F f[i-1][j]
    using namespace std;
    const int N=101010;
    const int qwq=303030;
    const int inf=0x3f3f3f3f;
    
    int n,m,d;
    int a[53];
    char s[N],z1[53],z2[53];
    int ch[N][10],ed[N],fa[N],cnt,now;
    ll f[53][25678][2];
    vector <int> e[N];
    queue <int> q;
    
    inline int read() {
    	int sum = 0, f = 1; char c = getchar();
    	while(c<'0' || c>'9') { if(c=='-') f = -1; c = getchar(); }
    	while(c>='0'&&c<='9') { sum = sum * 10 + c - '0'; c = getchar(); }
    	return sum * f;
    }
    
    void DFS(int u,bool flag) {
    	if(ed[u]) flag = 1;
    	if(flag) ed[u] = 1;
    	for(int i=0;i<e[u].size();i++) DFS(e[u][i],flag);
    }
    
    void AC() {
    	n = strlen(s+1);
    	for(int i=1;i+d/2-1<=n;i++) {
    		now = 0;
    		for(int j=i;j<=i+d/2-1;j++) {
    			int c = s[j] - '0';
    			if(!ch[now][c]) ch[now][c] = ++cnt;
    			now = ch[now][c];
    		}
    		ed[now] = 1;
    	}
    	for(int i=0;i<10;i++) if(ch[0][i]) q.push(ch[0][i]);
    	while(!q.empty()) {
    		int u = q.front(); q.pop();
    		for(int i=0;i<10;i++) {
    			int v = ch[u][i];
    			if(v) {
    				fa[v] = ch[ fa[u] ][i];
    				q.push(v);
    			}
    			else ch[u][i] = ch[ fa[u] ][i];
    		}
    	}
    	for(int i=1;i<=cnt;i++) e[ fa[i] ].push_back(i);
    	DFS(0,0);
    }
    
    int query() {
    	ll ans = 0;
    	memset(f,0,sizeof(f));
    	f[0][0][1] = 1;
    	for(int i=1;i<=d;i++) {
    		for(int j=0;j<=cnt;j++) {
    			for(int k=0;k<10;k++) {
    				int v = ch[j][k]; if(ed[v]) continue;
    				if(k<a[i]) f[i][v][0] += F[0] + F[1];
    				if(k>a[i]) f[i][v][0] += F[0];
    				if(k==a[i]) {
    					f[i][v][0] += F[0];
    					f[i][v][1] += F[1];
    				}
    			}
    		}
    	}
    	for(int i=0;i<=cnt;i++) ans += f[d][i][1] + f[d][i][0];
    	return ans;
    }
    
    int main() {
    	scanf("%s",s+1);
    	scanf("%s%s",z1+1,z2+1); d = strlen(z1+1);
    	AC();
    	ll shu1 = 0, shu2 = 0;
    	for(int i=1;i<=d;i++) a[i] = z1[i] - '0', shu1 = shu1 * 10 + a[i];
    	int h = d; while(a[h]==0) a[h] = 9, h--; a[h]--; shu1--;
    	ll ans1 = query();
    	for(int i=1;i<=d;i++) a[i] = z2[i] - '0', shu2 = shu2 * 10 + a[i];
    	ll ans2 = query();
    	cout<<(shu2-shu1) - (ans2-ans1);
    	return 0;
    }
    
    

    已略去模数。

  • 相关阅读:
    Timestamp,Date和String的互相转换
    从网址截取域名
    $.ajax()方法详解
    JS正则表达式详解
    List、Set、Map集合的遍历方法
    spring-security2配置精讲(转载)
    spring-security原理学习
    spring-security配置和原理简介
    三步法搞定CTF中的SQL注入题型
    两个局域网(办公网-IDC)安全互通方案2:by GRE and linux server&深入理解GRE
  • 原文地址:https://www.cnblogs.com/clever-sheep/p/12887764.html
Copyright © 2020-2023  润新知