题解:Atcoder Beginner Contest abc466 F - Many Mod Calculation

题目描述

给你整数 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 与 X 之间的整数 x 的个数,使得 f(x)=0 .

给你 T 个测试用例,请逐个求解。

数据范围

  • 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}
  • 所有输入值均为整数。

解题思路

核心思想

这个问题要求统计在 [1, X] 范围内,经过一系列取模操作后结果为 0 的数的个数。

关键观察:取模操作具有"分段常数"性质 。对于给定的模数 v,所有数 x 在区间 [kv, (k+1)v - 1] 内取模 v 的结果相同,都是 x - kv。

算法设计

用一个**优先队列(最大堆)**来维护区间信息,每个元素表示一个"状态区间":

  • val:当前状态值(取模后的结果)

  • cnt:有多少个原始数 x 能达到这个状态

具体步骤

  1. 初始化:

    复制代码
    pq.push({x + 1, 1});  // 表示原始区间 [0, X] 内有 X+1 个数,状态值为 X+1
  2. 逐个处理模数 :

    对每个 v = A[i],将当前所有状态 val 对 v 取模:

    • 从堆中取出最大的 val(因为我们需要处理所有相同的 val)

    • 合并所有相同 val 的计数

    • 分裂成新状态:

      • {v, val / v * cnt}:表示能整除的部分,取模后结果为 0

      • {val % v, cnt}:表示余数部分(如果余数不为 0)

  3. 统计答案 :

    最终堆中所有 val = 0 的状态计数之和,就是 f(x) = 0 的 x 的数量。

    由于统计的是 [0, X] 区间(包含 0),而题目要求 [1, X],所以答案为 总计数 - 1(减去 x = 0 的情况)。

完整代码

复制代码
#include<bits/stdc++.h>
#define fr1(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define fr2(i,a,b) for(int (i)=(a);(i)>=(b);(i)--)
#define fv(i,p) for(auto (i):(p))
#define ll long long
#define ull unsigned ll
#define pii pair<int,int>
#define pll pair<ll,ll>
#define _1st first
#define _2nd second
#define elif else if
#define debug cout<<endl<<"-------------------------------------------------------------"<<endl
using namespace std;

void solve() {
    int n;
    ll x;
    cin >> n >> x;
    vector<ll> a(n);
    for (ll &v : a) cin >> v;
    
    priority_queue<pll> pq;
    pq.push({x + 1, 1});
    
    for (ll v : a) {
        while (!pq.empty() && pq.top()._1st > v) {
            auto [val, cnt] = pq.top();
            pq.pop();
            while (!pq.empty() && pq.top()._1st == val) {
                cnt += pq.top()._2nd;
                pq.pop();
            }
            pq.push({v, val / v * cnt});
            if (val % v != 0) pq.push({val % v, cnt});
        }
    }
    
    ll ans = -1;
    while (!pq.empty()) {
        ans += pq.top()._2nd;
        pq.pop();
    }
    cout << ans << '\n';
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    
    int t;
    cin >> t;
    while (t--) solve();
    
    return 0;
}
相关推荐
神仙别闹9 分钟前
基于C++实现的贪吃蛇游戏平台
java·c++·游戏
王老师青少年编程1 小时前
2026年6月GESP真题及题解(C++三级): 选择题和判断题(题解)
c++·真题·gesp·答案·csp·三级·信奥赛
C++ 老炮儿的技术栈1 小时前
static的作用
c语言·c++·人工智能·单片机·c
程序员老陆2 小时前
C++ 未定义行为全景:对象模型、优化边界与工程防御
开发语言·c++·程序设计
小小龙学IT3 小时前
Go 语言 cgo 与 C/C++ 互操作深度解析:从 fwlib 直连到工业数采网关实战系列
c语言·c++·golang
Tisfy4 小时前
LeetCode 0301.删除无效的括号:二进制枚举 / 回溯
算法·leetcode·字符串·题解·回溯·括号匹配·二进制枚举
程序员老陆4 小时前
C++ 多线程通信方式全景:从共享内存到结构化协调
开发语言·c++
某不知名網友5 小时前
ROS2 核心通信:话题发布与订阅详解
开发语言·c++
C++ 老炮儿的技术栈5 小时前
char (*csConnectName)[256]; 和 char csConnectName [256] 有什么区别
java·c语言·开发语言·c++·人工智能·算法·c
大鹏说大话5 小时前
C++ STL 容器怎么选?vector、list、map、unordered_map 对比
开发语言·c++·list