Many Mod Calculation

Many Mod Calculation

1. 题目描述

给定整数 \(N, X\) 和一个长度为 \(N\) 的正整数序列 \(A = (A_1, A_2, \dots, A_N)\)。

定义函数:

\[f(x) = (\cdots((x \bmod A_1) \bmod A_2) \cdots) \bmod A_N \]

即对 \(x\) 依次对 \(A_1, A_2, \dots, A_N\) 取模。

目标:求在 \(1 \le x \le X\) 范围内,满足 \(f(x) = 0\) 的整数 \(x\) 的个数。

输入格式

  • 第一行:一个整数 \(T\),表示测试用例数。
  • 每个测试用例:
    • 第一行:两个整数 \(N, X\)
    • 第二行:\(N\) 个整数 \(A_1, A_2, \dots, A_N\)

输出格式

对于每个测试用例,输出一行答案。

约束条件

  • \(1 \le T \le 2 \times 10^5\)
  • \(1 \le N \le 2 \times 10^5\)
  • 所有测试用例的 \(N\) 之和不超过 \(2 \times 10^5\)
  • \(1 \le X \le 10^{18}\)
  • \(1 \le A_i \le 10^{18}\)
  • 所有输入值均为整数。

2. 核心思路

2.1 区间表示法

将整数集合用 连续区间 \([0, \text{len})\) 表示,代表整数 \(0, 1, \dots, \text{len}-1\)。
每个区间附带一个 出现次数 cnt,表示该区间内的每个整数都出现了 cnt 次。

初始状态:区间 \([0, X+1)\),出现次数为 \(1\),表示考虑整数 \(0, 1, \dots, X\),最后再减去 \(x=0\)(因为题目要求从 \(1\) 开始)。

2.2 取模操作分解

对区间 \([0, x)\)(出现 cnt 次)应用取模 \(M\)(当 \(x > M\) 时):

  • 商:\(q = \left\lfloor \frac{x}{M} \right\rfloor\),余数:\(r = x \bmod M\)
  • 分解为:
    • \(q\) 个完整的区间 \([0, M)\),每个计数 cnt → 相当于 \([0, M)\) 总计数增加 cnt * q
    • 若 \(r > 0\),新增区间 \([0, r)\),计数 cnt

关键性质:余数区间长度 \(r < \frac{x}{2}\),因此每个区间最多被分解 \(O(\log X)\) 次。

2.3 高效维护数据结构

使用 std::map<long long, long long>:

  • 键(Key):区间长度 len
  • 值(Value):该区间的出现次数 cnt
  • map 自动按键升序排序,因此可通过 rbegin() 快速获取当前最大区间长度。

优势:

  • 自动合并相同长度区间(例如 mp[M] += cnt * q)
  • 无需优先队列,避免重复元素堆积,提高效率。

3. 预处理:过滤无用的模数

若序列中存在 \(A_i \ge A_{i-1}\),则取模 \(A_i\) 不会改变任何数的值,因为所有数已经小于等于 \(A_{i-1} \le A_i\),取模不变。

因此,只需保留 严格递减 的子序列。


#include <bits/stdc++.h>
using namespace std;
using ll = long long;

void solve() {
    ll n, x; cin >> n >> x;
    vector<ll> a;
    for(int i = 0; i < n; i ++) {
        ll A; cin >> A;
        if(a.empty() || A < a.back()) a.push_back(A);
    }
    int k = a.size();
    map<ll, ll> mp; mp[x + 1] = 1;
    for(int i = 0; i < k; i ++) {
        ll A = a[i];
        while(!mp.empty() && mp.rbegin()->first > A) {
            auto it = prev(mp.end()); 
            ll len = it->first, cnt = it->second;
            mp.erase(it);
            ll q = len / A, r = len % A;
            mp[A] += q * cnt;
            if(r > 0) mp[r] += cnt;
        }
    }
    ll ans = 0;
    for(auto &p : mp) ans += p.second;
    cout << ans - 1 << endl;
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    int t = 1; cin >> t;
    while(t --) solve();
    return 0;
}
posted @ 2026-10-26 14:18  thenrify  阅读(3)  评论(0)    收藏  举报