10.10 爆零赛(2023 炼石计划 NOIP 模拟赛 #2)

炼石计划 9 月 10 日 NOIP 模拟赛 #2【补题】 - 比赛 - 梦熊联盟 (mna.wang)

模拟赛恒等式:\(0+0+0+0=0\)

复盘

T1 好像可做。有个显然的 \(n^2\) DP。推式子的时候猜到了 \(\gcd = 1\) 的做法。进一步尝试正解未果。

T2 一眼只会爆搜。想到了 \(b \times b!\) 的做法,应该能过 \(n \le 11\) 的点(实际上这也是出题人的意图)。但是测极限数据 3s,卡常未果。

T3, T4 一分不会。想的时间很少就放弃了。

剩下的时间基本都在 T2 优化(或者说卡常)。

总结

你说呢?

有个好的:T1 做完了正解的第一步。

题解

A. 数位(DP,前缀和,数论)

有 DP:\(f(i, 0/1)\) 表示考虑前 \(i\) 个数,最后一个段是否是倍数串。

转移枚举 \(j \in [0, i)\)。如果 \((j, i]\) 是倍数串那么 \(f(j,0)+f(j,1) \to f(i, 1)\),否则 \(f(j, 1) \to f(i, 0)\)

可以轻易 \(\mathcal O(n^2)\) 预处理 \(g(j, i) = \operatorname{val}(j, i] \bmod D = (g(j,i-1) \times 10 + a_j) \bmod D\)。转移也是 \(\mathcal O(n^2)\)。考虑优化。

考虑什么时候 \(\operatorname{val}(j, i] \bmod d = 0\),即 \((j, i]\) 是一个倍数串。

类似字符串 Hash。令 \(p(i) = \operatorname{val}[i,n]\)

首先考虑 \(\gcd(10, D) =1\) 的情况。不难发现 \((j, i]\) 是倍数串等价于 \(p(j + 1) \equiv p(i+1) \pmod D\)读者自证不难。

然后怎么做?上面代码可以写成:

for (int i = 1; i <= n; ++ i ) {
	f[i][p[1] == p[i + 1]] = 1;
	for (int j = 1; j < i; ++ j ) {
		if (p[j + 1] == p[i + 1]) f[i][1] = (f[i][1] + f[j][0] + f[j][1]) % P;
		else f[i][0] = (f[i][0] + f[j][1]) % P;
	}
}

前缀和优化即可。

考虑 \(\gcd(10, D) \ne 1\) 的情况。不妨设 \(D = 2^x5^yD'\),其中 \(10 \not \mid D’\)。因为 \(D\) 是百万级别所以 \(x, y \le 20\)

那么 \(p(j + 1) \equiv p(i+1) \pmod D\) 当且仅当下面的条件都满足:

\[\begin{align} p(j + 1) &\equiv p(i+1) \pmod {2^x}\\ p(j + 1) &\equiv p(i+1) \pmod {5^y}\\ p(j + 1) &\equiv p(i+1) \pmod {D'}\\ \end{align} \]

注意到当一个串的长度超过 \(20\) 后,前两个条件是否满足已经可以通过其低 \(20\) 位确定了。这是因为当 \(x \le 20\)\(a_i \times 10^{21} \bmod 2^x = 0\)\(5\) 也同理。也就是说 \(20\) 位之后是多少,都对整个值模 \(2^x,5^y\) 后的结果没有影响。

所以当长度 \(\le 20\)(即 \(i - j \le 20\))时暴力。当长度 \(> 20\) 时,先判断其第 \(20\) 位是否满足前两个条件。注意到 \(\gcd(D', 10) = 1\)。于是又转化了最开始的问题。分类讨论一下再前缀和即可。

提交记录 #669942 - 梦熊联盟 (mna.wang)

// Problem: A. 2024--[炼石计划--NOIP模拟二]--T1 -- 数位
// Contest: LibreOJ
// URL: https://mna.wang/contest/223/problem/1
// Memory Limit: 512 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

#pragma GCC optimize(3)

#define int long long

using namespace std;

const int N = 1e5 + 10, M = 1e6 + 10, P = 1e9 + 7;

int n, a[N], d;
int f[N][2];
int val[N][22];
int N2[N][22], N5[N][22], ND[N];
int sum0[M], sum1[M];

int solve() {
	string str;
	cin >> str >> d;
	
	for (int i = 0; i < d; ++ i ) sum0[i] = sum1[i] = 0;
	
	n = str.size();
	for (int i = 0; i < n; ++ i ) a[i + 1] = str[i] - '0';
	
	int nd = d, x = 1, y = 1, l1 = 0, l2 = 0;
	while (nd % 2 == 0) nd /= 2, x *= 2, l1 ++ ;
	while (nd % 5 == 0) nd /= 5, y *= 5, l2 ++ ;
	int lim = max(l1, l2) + 1;
	
	f[0][1] = 1;
	
	int sum = 0;
	for (int i = 1; i <= n; ++ i )
		for (int j = 1; i + j - 1 <= n && j <= lim; ++ j ) {
			val[i][j] = (val[i][j - 1] * 10 + a[i + j - 1]) % d;
			N2[i][j] = (N2[i][j - 1] * 10 + a[i + j - 1]) % x;
			N5[i][j] = (N5[i][j - 1] * 10 + a[i + j - 1]) % y;
		}
	
	ND[n + 1] = 0;
	for (int i = n, j = 1; i; -- i, j = j * 10ll % nd) {
		ND[i] = (ND[i + 1] + j * a[i]) % nd;
	}
	
	for (int i = 1; i <= n; ++ i ) {
		f[i][0] = f[i][1] = 0;
		 
		for (int j = max(1ll, i - lim + 1); j <= i; ++ j )
			if (val[j][i - j + 1] == 0) f[i][1] = (f[i][1] + f[j - 1][0] + f[j - 1][1]) % P;
			else f[i][0] = (f[i][0] + f[j - 1][1]) % P;
		
		if (i - lim >= 0) {
			f[i][0] = (f[i][0] + sum) % P;
			if (N2[i - lim + 1][lim] == 0 && N5[i - lim + 1][lim] == 0) {
				f[i][1] = (f[i][1] + sum0[ND[i + 1]] + sum1[ND[i + 1]]) % P;
				f[i][0] = (f[i][0] - sum1[ND[i + 1]] + P) % P;
			}
		}
		
		if (i >= lim) {
			sum = (sum + f[i - lim][1]) % P;
			sum0[ND[i - lim + 1]] = (sum0[ND[i - lim + 1]] + f[i - lim][0]) % P;
			sum1[ND[i - lim + 1]] = (sum1[ND[i - lim + 1]] + f[i - lim][1]) % P;
		}
	}
	
	return (f[n][0] + f[n][1]) % P;
}

signed main() {
//	freopen("ex_a.in", "r", stdin);
//	freopen("AC.out", "w", stdout);
	freopen("digit.in", "r", stdin);
	freopen("digit.out", "w", stdout);
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

	int T;
	cin >> T;
	while (T -- ) cout << solve() << '\n';
	return 0;
}
posted @ 2024-10-10 19:45  2huk  阅读(185)  评论(3)    收藏  举报
2048 Game
Score
0
Best
0