题解:洛谷 AT_abc466_f Many Mod Calculation
【题目来源】
洛谷:AT_abc466_f [ABC466F] Many Mod Calculation
【题目描述】
You are given integers \(N,X\) and a length-\(N\) sequence of positive integers \(A=(A_1,A_2,\ldots,A_N)\).
For a non-negative integer \(x\), define \(f(x)=(\ldots((x \bmod A_1) \bmod A_2) \ldots ) \bmod A_N\).
Find the number of integers \(x\) between \(1\) and \(X\), inclusive, such that \(f(x)=0\).
You are given \(T\) test cases; solve each of them.
给定整数 \(N, X\) 和一个长度为 \(N\) 的正整数序列 \(A=(A_1,A_2,\ldots,A_N)\)。
对于非负整数 \(x\),定义 \(f(x)=(\ldots((x \bmod A_1) \bmod A_2) \ldots ) \bmod A_N\)。
求满足 \(1 \le x \le X\) 且 \(f(x)=0\) 的整数 \(x\) 的个数。
给定 \(T\) 组测试用例,请分别求解。
【输入】
The input is given from Standard Input in the following format:
\(T\)
\(\text{case}_1\)
\(\text{case}_2\)
\(\vdots\)
\(\text{case}_T\)
Each test case is given in the following format:
\(N\) \(X\)
\(A_1\) \(A_2\) \(\ldots\) \(A_N\)
【输出】
Output the answers for the test cases in order, separated by newlines.
【输入样例】
4
3 7
5 2 3
9 31415
9 9 8 2 4 4 3 5 3
1 1000000000000000000
1
9 20260405
3141 5926 5358 9793 2384 6264 3383 2795 288
【输出样例】
4
17452
1000000000000000000
77403
【算法标签】
提高+ #递归
【核心思想】
-
问题分析:给定 \(N\) 个模数 \(A_1, A_2, \ldots, A_N\) 和查询上界 \(X\),定义 \(f(x) = ((\ldots((x \bmod A_1) \bmod A_2) \ldots) \bmod A_N)\),求 \([1, X]\) 内满足 \(f(x) = 0\) 的整数个数。这是一个递归计数 + 模数链压缩问题,核心在于利用连续取模的单调性压缩冗余模数,并通过递归分层计算满足条件的数的个数。
-
算法选择:
- 前缀最小值压缩:构建严格递减序列 \(b\),只保留对结果有实际影响的模数
- 递归预处理 + 二分查找:从右往左预处理每个模数区间内的满足条件数,查询时通过二分定位当前适用的模数层级
-
关键步骤:
- 模数压缩:遍历 \(A\),仅当 \(A_i < b_k\) 时加入 \(b\),得到严格递减序列 \(b_1 > b_2 > \ldots > b_k\)
- 预处理 \(f\) 数组(从右往左):
- \(f[i] = calc(b_i - 1, i + 1)\),即 \([0, b_i-1]\) 内满足 \(f(x) = 0\) 的数的个数
- 递归终止条件:当没有更小的模数时,\([0, value]\) 内满足条件的数只有 \(0\) 及其倍数
- 查询函数 \(calc(value, start)\):
- 通过二分查找 \(find(start, value)\) 定位第一个 \(\leq value\) 的模数 \(b_i\)
- \(value = q \times b_i + r\),累加 \(q \times f[i]\)(完整周期贡献),然后处理余数 \(r\),进入下一层 \(i+1\)
- 答案计算:\(calc(X, 1) - 1\)(减去 \(x=0\) 的情况)
-
时间/空间复杂度:
- 时间复杂度:\(O(T \cdot (N + k^2))\),预处理 \(O(N)\),每次查询递归深度 \(O(k)\),每层二分查找 \(O(\log k)\)
- 空间复杂度:\(O(k)\),存储压缩后的模数序列和 \(f\) 数组
-
递归计数与模数链压缩的核心思想:
- 模数冗余消除:若 \(A_i \leq A_{i+1}\),则 \(x \bmod A_i < A_i \leq A_{i+1}\),后续对 \(A_{i+1}\) 取模不改变结果。因此只需保留严格递减的模数,将 \(N\) 压缩为 \(k \leq \log(\max A)\) 量级
- 周期性分层计数:对于递减模数 \(b_i > b_{i+1}\),区间 \([0, b_i-1]\) 可按 \(b_{i+1}\) 划分为若干完整周期。每个周期 \([t \cdot b_{i+1}, (t+1) \cdot b_{i+1} - 1]\) 内的满足条件数相同,均为 \(f[i+1]\),因此完整周期贡献为 \(\lfloor value/b_{i+1} \rfloor \times f[i+1]\)
- 余数递归处理:余数部分 \(r < b_{i+1}\) 进入更深层模数处理,形成自然的递归结构
- 二分定位优化:通过二分查找快速定位当前 \(value\) 适用的最小模数,避免逐层遍历
- 适用于连续模运算、具有层次化周期结构的大规模计数问题,核心是通过单调性压缩和周期性分解实现 \(O(k^2)\) 的高效计算
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 200005; // 常量:最大序列长度
int t; // t: 测试用例数量
int n, x; // n: 序列长度; x: 查询上界
int k; // k: 有效模数个数(递减前缀最小值序列长度)
int b[N]; // b[i]: 递减的有效模数序列(b[1..k])
int f[N]; // f[i]: [0, b[i]-1] 内满足 f(x)=0 的数的个数
// 二分查找:在 b[start..k] 中找到第一个 <= value 的模数位置
int find(int start, int value)
{
int left = start, right = k + 1;
while (left < right)
{
int mid = (left + right) / 2;
if (b[mid] <= value) // 如果 mid 位置的模数 <= value,向左缩小范围
right = mid;
else // 否则向右缩小范围
left = mid + 1;
}
return left;
}
// 计算 [0, value] 内满足 f(x)=0 的数的个数,从第 start 个模数开始处理
int calc(int value, int start)
{
int ans = 1; // 初始包含 0
int i = start;
while (i <= k)
{
i = find(i, value); // 找到第一个 <= value 的有效模数位置
if (i > k) // 没有更小的模数了,退出
break;
// value = q * b[i] + r,每个完整周期有 f[i] 个满足条件的数
ans += value / b[i] * f[i];
value %= b[i]; // 处理余数部分
i++; // 进入下一层模数
}
return ans;
}
signed main()
{
cin >> t; // 读入测试用例数量
while (t--) // 循环处理每组测试用例
{
cin >> n >> x; // 读入序列长度和查询上界
k = 0; // 重置有效模数计数器
for (int i = 1; i <= n; i++) // 读入模数序列,构建递减前缀最小值
{
int value;
cin >> value;
if (k == 0 || value < b[k]) // 只保留严格递减的模数
b[++k] = value;
}
for (int i = k; i >= 1; i--) // 从右往左预处理 f 数组
{
f[i] = calc(b[i] - 1, i + 1); // f[i] = [0, b[i]-1] 内满足条件的个数
}
// 答案:[1, x] 内满足条件的个数 = [0, x] - {0}
cout << calc(x, 1) - 1 << endl;
}
return 0;
}
【运行结果】
4
3 7
5 2 3
9 31415
9 9 8 2 4 4 3 5 3
1 1000000000000000000
1
9 20260405
3141 5926 5358 9793 2384 6264 3383 2795 288
4
17452
1000000000000000000
77403
浙公网安备 33010602011771号