zoukankan      html  css  js  c++  java
  • 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;
    }
    
    

    已略去模数。

  • 相关阅读:
    021.day21 反射 Class类 反射常用操作
    020.day20 线程概述 多线程优缺点 线程的创建 线程常用方法 生命周期 多线程同步
    019.day19 缓冲流 对象流 标准输入输出流
    018.day18 map集合如何实现排序 File类 IO流 字节流 字符流 编码
    017.day17 Map接口 克隆 treeSet集合排重缺陷
    016.day16 HashSet TreeSet 比较器Comparable Comparator
    015.day15
    014.day14
    013.day13
    线程
  • 原文地址:https://www.cnblogs.com/clever-sheep/p/12887764.html
Copyright © 2011-2022 走看看