同余最短路

这个东西是用来解决类似有 \(\sum_{i = 1}^n{a_ix_i} = V\) 类似这个限制下的一些最优化问题/判定的。但是这个东西的复杂度与 \(\min\{a_i\}\) 有关,所以什么二元不定方程组求解还是用 \(\rm exgcd\)

problem: 给出 \(n\) 个整数 \(a_1, \dots, a_n\),求是否存在一组非负整数 \(x_1, \dots, x_n\),使得 \(\sum_{i = 1}^n{a_ix_i} = V\)。其中 \(\min\{a_i\} \le 5 \times 10^5, n \le 100, V \le 10^{18}\)

首先不难发现假设可以通过一些组合达到 \(s\),那么显然 \(s + ka_1(k \ge 0)\) 显然都可以达到。那么现在相当于要求 \(v \equiv V \pmod {a_1}\) 的可以被组合出的 \(v \le V\)。于是那么这个东西相当于一个模 \(a_1\) 意义下的最短路。这个显然可以对于 \(0 \le i < a_1, 2 \le j \le n\),都连一条到 \((i + a_j) \bmod a_1\) 的边。跑最短路即可。时间复杂度为 \(O(nm\log nm)\)。当然也可以令 \(a\) 最大的为基准物品,这样就可以 \(\texttt{0-1bfs}\) 做到 \(O(nm)\)

但这个不够优秀。我们需要一个不基于选择物品就可以做到 \(O(nm)\) 的算法。

不难发现一个大小为 \(x\) 的物品,会使得出现 \(\gcd(x, a_1)\) 个环(证明考虑起点位置),于是不妨对每个环单独考虑,那么着相当于一个完全背包,于是将整个环复制一遍,注意到显然不会绕着环走一圈,于是装两圈即可。这样每个点只被考虑了两遍,时间复杂度就是 \(O(nm)\) 的了。

qwq
#include<bits/stdc++.h>
#define ll long long
#define int long long 
#define pir pair<int, int>
#define pb emplace_back
using namespace std;

const int N = 5e5 + 5, M = 20 + 5, INF = 1e18;

int n, l, r, a[M], f[N], mod = INF;

inline void chkmin(int& x, int y){if(y < x) x = y;}

signed main(){
  ios::sync_with_stdio(0);
  cin.tie(0); cout.tie(0);
  cin >> n >> l >> r;
  for(int i = 1; i <= n; i++) cin >> a[i];
  sort(a + 1, a + n + 1); int st = 1;
  while(st <= n && (!a[st])) st++;
  if(st == n + 1){cout << (l == 0); return 0;}
  mod = a[st];
  for(int i = 0; i < mod; i++) f[i] = INF;
  f[0] = 0;
  for(int i = st + 1; i <= n; i++){
    for(int j = 0, lim = __gcd(a[i], mod); j < lim; j++){
      for(int x = j, c = 0; c < 2; c += (x == j)){
        int p = (x + a[i]) % mod;
        chkmin(f[p], f[x] + a[i]); x = p;
      }
    }
  }
  int ans = 0;
  for(int i = 0; i < mod; i++){
    if(r >= f[i]) ans += (r - f[i]) / mod + 1;
    if(l > f[i]) ans -= (l - 1 - f[i]) / mod + 1;
  } cout << ans;

  return 0;
}

posted @ 2025-02-24 22:00  Little_corn  阅读(29)  评论(0)    收藏  举报