CF585F Digits of Number Pi

Posted clever-sheep

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
}

以上是关于CF585F Digits of Number Pi的主要内容,如果未能解决你的问题,请参考以下文章

[CF 582D]Number of Binominal Coefficients

cf Maximum Sum of Digits

数学CF27E Number With The Given Amount Of Divisors

CF582D Number of Binominal Coefficients 库默尔定理 数位dp

CF582D Number of Binominal Coefficients 库默尔定理 数位dp

CF 489 C Given Length and Sum of Digits... 贪心