当前位置: 首页 > news >正文

洛谷题目:P2371 [CTSC 中国信息学国家集训队] 墨墨的等式 题解(本题难)

题目传送门:

P2371 [国家集训队] 墨墨的等式 - 洛谷 (luogu.com.cn)

前言:

这道题主要求我们计算在区间  [l,r]  中 b  能使等式  {\textstyle \sum_{i=1}^{n}}a_{i} x_{i}=b  存在非负整数解,总体来难度的话还是挺大的,下面为大家详细讲解解题思路。

本题狠心思路:

        我们采用同余最短路的方法来解决这个问题。同余最短路的狠心思想是利用余数的心智,将一个复杂的线性组合存在性问题转化为图论中的最短路问题,通过构件图并计算最短路径来确定满足条件的 b  的个数。

#具体步骤:

        1、选择模数:

                给定的  n  个系数  a_{1},a_{2}\cdots ,a_{n}  中选择一个非零的  a_{i}  作为模数 mod  ,通常选择最小的非零  a_{i}  ,作为模数可以使后续构建的图的节点数相对较少,减少 计算量。

        2、构建图:

                将所有可能的余数 0 到  mod-1  看做图中的节点。对于每个  a_{j} (1\le j\le n)  ,从节点 k 向节点  (k+a_{j})mod  mod  连着一条长度为 a_{j}  的边。

        3、求解最短路:

                        我们使用 Dijkstra 等最短路算法,以 0 为起点,计算到每个节点  i  的最短路路径的长度  dis[i]  。这里的  dis[i]  表示能得到余数为  i  的最小的  b  值。具体来说,从起点 0 开始,不断更新到其他节点的最短距离,当遍历完所有可能的边后,就能得到从 0 到每个余数节点的最短路径长度,当遍历完所有可能的边后后,就能得到从 0 到每个余数节点的最短路径长度。

        4、统计答案:

                对于给定的区间  [l,r]  ,遍历所有余数 i  (0\le i < mod)   。对于每个余数  i  ,能的到余数为  i  的最小的  b  的值是  dis[i]  ,那么满足   dis[i]+k \times mod \in [l,r]   的非负整数  k

的个数就是该余数对应的满足条件  b  的个数。我们可以通过计算满足不等式    l \le dis[i]+k \times mod \le r   的  k  的范围来得到这个个数。具体计算的时候,我们要先计算  k  的下线     left = max(0,\left \lceil \frac{l-dis[i]}{mod} \right \rceil )     和上限   right=\left \lfloor \frac{r-dis[i]}{mod} \right \rfloor   ,则满足条件的  k  的个数为   max(0,right - left +1)   ,将所有余数对应的个数累脚起来就是最答案。

##示例解释:

        假设我们输入 n=2,t=5,r=10,a_{1}=3,a_{2}=5  。

                选择  mod=3  作为模数。

                构建图:

                        从节点 0 向节点  (0,3)mod3=0  连长度为 3 的边,向节点   (0,5)mod3=2  连长度为 5 的边。

                        从节点 1 向节点 (1+3)mod3=1  连长度为 3 的边,向节点   (1+5)mod3=0   连长度 5 的边。

                        从节点 2 向节点  (2+3)mo3=2   连长度为 3 的边,向节点  (2+5)mod3=1   连长度为 5 的边。

        使用 Dijkstra 算法计算最短路,得到    dis[0]=0,ds[1]=5,dis[2]=3  。

        统计答案:

                对于  i=0  ;

                  计算   left=max(0,\left \lceil \frac{5-0}{3} \right \rceil )=2,right=\left \lfloor \frac{10-0}{3} \right \rfloor   ,满足条件的 k 的有 3 - 2 + 1 = 2 个,对应的 b 为 2 * 3 = 6 和 3 * 3 = 9. 。

                对于 i=1  ;   

                        计算  left=max(0,\left \lceil \frac{5-3}{3} \right \rceil ),right=\left \lfloor \frac{10-5}{3} \right \rfloor =1     ,满足条件的 k 有 1 - 0 + 1 = 2个,对应的 b 为 5 + 0 * 3 = 5 和 5 + 1 * 3 = 8.

                对于  i=2  ;

                        计算  left = max()0,\left \lceil \frac{5-3}{3} \right \rceil )=1,right = \left \lfloor \frac{10-3}{3} \right \rfloor =2    ,满足条件的 k 有 2 - 1 + 1 =2 个,但其中 b = 3 + 1 * 3 =6 和 b = 3 + 2 * 3 = 9 与前面重复,只新增 1 个不重复的 b 。

                最终满足条件的 b 的个数为 5 。

###复杂度:

        1、时间复杂度:

                O(mod \times n \times log(mod))   。

        2、空间复杂度:

                O(mod \times n) 。

####代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

const int MAX = 15;
const int MAN = 5e5 + 5;

struct Node {
	    LL dis;
    int u;
    Node(LL _dis, int _u) : dis(_dis), u(_u) {}
    bool operator>(const Node& other) const {
        return dis > other.dis;
    }
};

int n;
LL l, r;
int a[MAX];
LL dis[MAN];

// 计算区间 [l, r] 内满足条件的 b 的数量
LL c(LL l, LL r, LL base, LL mod) {
    if (base > r) return 0;
    LL left = max(0LL, (l - base + mod - 1) / mod);
    LL right = (r - base) / mod;
    return max(0LL, right - left + 1);
}

int main() {
    cin >> n >> l >> r;
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
    }
    // 找到最小的非零 a[i] 作为模数
    int mod = 0;
    for (int i = 0; i < n; ++i) {
        if (a[i] > 0 && (mod == 0 || a[i] < mod)) {
            mod = a[i];
        }
    }
    if (mod == 0) {
        cout << (l == 0 && r == 0 ? 1 : 0) << endl;
        return 0;
    }

    // 初始化距离数组
    fill(dis, dis + mod, LLONG_MAX);
    dis[0] = 0;

    // 使用优先队列进行 Dijkstra 算法
    priority_queue<Node, vector<Node>, greater<Node>> pq;
    pq.push(Node(0, 0));

    while (!pq.empty()) {
        Node cur = pq.top();
        pq.pop();
        LL d = cur.dis;
        int u = cur.u;
        if (d > dis[u]) continue;

        for (int i = 0; i < n; ++i) {
            if (a[i] == 0) continue;
            int v = (u + a[i]) % mod;
            LL N = d + a[i];
            if (N < dis[v]) {
                dis[v] = N;
                pq.push(Node(N, v));
            }
        }
    }

    // 统计满足条件的 b 的数量
    LL ans = 0;
    for (int i = 0; i < mod; ++i) {
        ans += c(l, r, dis[i], mod);
    }

    cout << ans << endl;
    return 0;
}

                        

相关文章:

  • GoogleAgent-quickstart 学习笔记
  • 23种GoF设计模式
  • 【开题报告+文档+源码】基于SpringBoot教学评价评教系统
  • 数据采集与Web组态显示的技术实现路径解析
  • Android 16应用适配指南
  • 数据集成工具推荐,支持数据库、API、消息文件等集成技术,并具备低代码与可视化配置特性
  • ps 人像学习
  • 方案精读:51页 财政数据信息资源目录数据标准存储及大数据资产化规划方案【附全文阅读】
  • langchain之agent系列:zero-shot-react-description agent
  • 【models】Transformer 之 各种 Attention 原理和实现
  • LLM KV Cache压缩技术解析:Multi-Head Key-Value共享方案
  • openharmony—release—4.1开发环境搭建(踩坑记录)
  • 软考 系统架构设计师系列知识点 —— 设计模式之抽象工厂模式
  • WPS复制粘贴错误 ,文件未找到 mathpage.wll
  • Android学习22 -- perfetto
  • 【自动驾驶 机器人】速度规划 |梯形/S型速度曲线
  • python中的字符串
  • 嵌入式面试笔试那点事2:2025.4.13
  • Vue事件修饰符课堂练习
  • golang-context详解
  • 百年传承,再启新程,参天中国迎来2.0时代
  • 李开复出任福耀科技大学理事会理事,助力学校AI战略
  • 广东省副省长刘红兵跨省任湖南省委常委、宣传部部长
  • 走访中广核风电基地:701台风机如何乘风化电,点亮3000万人绿色生活
  • 习近平:在庆祝中华全国总工会成立100周年暨全国劳动模范和先进工作者表彰大会上的讲话
  • 程璧“自由生长”,刘卓辉“被旋律牵着走”