首页 > 其他分享 >同余最短路的转圈法

同余最短路的转圈法

时间:2023-07-18 18:55:32浏览次数:45  
标签:gcd na 短路 cap 更新 转圈 同余 rep mathrm

学习自 Alex_Wei 的博客

同余最短路模板题:[国家集训队] 墨墨的等式

已知长为 \(n\) 的序列 \(a\)。对于不定方程 \(\sum\limits_{i=1}^na_ix_i=b\;(x_i\ge0)\),问有多少 \(b\in[l,r]\) 可以使得方程有解。

\(n\le12\),\(a_i\le5\times10^5\),\(l,r\le10^{12}\)。

本文默认取模得到的结果都是自然数

首先把询问拆成区间 \([0,l-1]\) 和 \([0,r]\),只考虑求区间 \([0,l]\) 的合法数。

设集合 \(B=\{x\in\mathbb Z\mid 当\,b=x\,时原方程有解\}\)。显然如果 \(r\in B\) ,那么 \(r+a_i\in B\)。

设集合 \(\mathrm Z_p^i=\{x\in \mathbb{Z}\mid x\equiv i\pmod p\}\),即模 \(p\) 余 \(i\) 的剩余类。显然有结论:

\[i\equiv j\!\!\!\pmod p\iff \mathrm Z_p^i=\mathrm Z_p^j \]

\[\bigcup\limits_{i=k}^{k+p-1}\mathrm Z_p^i=\mathbb Z \]

我们随便选一个 \(a_i\),方便起见我们选择 \(a_1\)。设 \(f(i)\) 表示 \(\mathrm Z_{a_1}^i\cap B\) 的最小元素,方便起见令 \(i\in[0,a_1)\)。

显然 \(\forall k\ge 0,f(i)+ka_1\in\mathrm Z_{a_1}^i\cap B\)。因此如果我们求出了 \(f(i)\),对于每个 \(i\) 我们都可以算出满足 \(Z_{a_1}^i\cap B\cap[0,l]\) 的大小,即模 \(a_1\) 余 \(i\) 的答案数。因为 \(\bigcup\limits_{i=0}^{a_1-1}\mathrm Z_{a_1}^i=\mathbb Z\),所以这些答案加起来不重不漏。

考虑如何求 \(f_i\),不难发现:

\[f(0)=0 \]

\[f(i)=\min_{j=2}^nf((i-a_j)\bmod a_1)+a_j \]

一种方法是建图,用最短路解决。点数为 \(a_i\),边数为 \(na_i\),时间复杂度是 Dijkstra 的 \(O(na_1\log na_1)\) 或 SPFA 的 \(O(na_1^2)\)。

但是还有另一种方式,考虑逐个添加 \(a\),设 \(g(j,i)\) 表示只有前 \(j\) 个数时的 \(f(i)\),现在考虑从 \(j-1\) 到 \(j\),添加一个 \(a_j\) 会发生的转移:

\[g(j-1,i)+ka_j\rightarrow g(j,(i+ka_j)\bmod a_1)\quad(k\ge0) \]

(箭头表示“更新”)

对于每个 \(i\),考虑从 \(0\) 开始枚举 \(k\),不难发现 \(k=\dfrac{a_1}{\gcd(a_1,a_j)}\) 时就可以停下了,因为此时 \(i+ka_j\equiv i\pmod{a_1}\),再往下走只会把之前更新过的再更新一遍,但是由于此时的 \(k\) 比上一次更新时大,所以这些更新不会再起效果。

由于模数相同时,不同的剩余类交集为空,所以考虑对于每个剩余类中的下标分别更新。形式化地说,就是对每个 \(x\in[0,a_j)\),分别更新 \(f_i\),其中 \(i\in\mathrm Z_{a_j}^x\)。从完全背包的角度考虑这个问题,压掉第一维,得到转移 \(f(i)+a_j\rightarrow f((i+a_j)\bmod a_{1})\)。从 \(i\) 开始走 \(k=\dfrac{a_1}{\gcd(a_1,a_j)}\) 步可以更新完 \(i\) 能更新的点,那么从 \(i\) 开始走 \(2k\) 步,也就是“多走一圈”,就相当于从 \(i\) 所在的剩余类中的每个点都走了 \(k\) 步,也就可以更新完这个剩余类中的每个点。

for(int p = i, c = 0; c < 2; c += p == i) {
    gmin(f[(p + a[j]) % a[1]], f[p] + a[j]);
    p = (p + a[j]) % a[1];
}

每次可以更新 \(\dfrac{a_1}{\gcd(a_1,a_j)}\) 个位置,所以只需要枚举前 \(\gcd(a_1,a_j)\) 个位置进行更新即可。

时间复杂度 \(\Theta(na_1)\)。可以先排序让 \(a_1\) 最小,减小常数。

#include<bits/stdc++.h>
#define endl '\n'
#define rep(i, s, e) for(int i = s, i##E = e; i <= i##E; ++i)
#define int long long
#define gmin(x, y) (x = min(x, y))
using namespace std;
constexpr int N = 15, A = 5e5;
int n, a[N], l, r, f[A];
signed main() {
#ifdef ONLINE_JUDGE
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
#endif
    cin >> n >> l >> r;
    rep(i, 1, n) cin >> a[i];
    sort(a + 1, a + n + 1);
    memset(f, 0x3f, sizeof f);
    f[0] = 0;
    rep(j, 2, n) rep(i, 0, __gcd(a[1], a[j]) - 1)
        for(int p = i, c = 0; c < 2; c += p == i) {
            gmin(f[(p + a[j]) % a[1]], f[p] + a[j]);
            p = (p + a[j]) % a[1];
        }
    int ans = 0;
    --l;
    rep(i, 0, a[1] - 1) {
        if(l >= f[i]) ans -= (l - f[i]) / a[1] + 1;
        if(r >= f[i]) ans += (r - f[i]) / a[1] + 1;
    }
    cout << ans << endl;
    return 0;
}

标签:gcd,na,短路,cap,更新,转圈,同余,rep,mathrm
From: https://www.cnblogs.com/untitled0/p/tyzdl.html

相关文章

  • 复杂最短路做题笔记
    1.CF609EMinimumspanningtreeforeachedgeluogu传送门CodeForces传送门题意给你\(n\)个点,\(m\)条边,对于编号位i的边求出必须包含i的最小生成树权值和。很好理解,不做赘述。题解前置芝士:Kruskal算法求最小生成树,ST表倍增。首先,我们不考虑每条边的限制,先将整张图的......
  • jquery怎么实现点查询时页面淡化并转圈提示正在加载
    jQuery实现点查询时页面淡化并转圈提示正在加载在现代的网页应用中,用户体验是至关重要的一部分。当用户进行查询操作时,如果页面没有及时给出反馈,用户可能会感到焦虑和不耐烦。因此,在进行查询时,我们可以使用jQuery来实现页面的淡化效果,并显示一个加载提示,以提升用户体验。实际问题......
  • abc088 <bfs 最短路>
    题目D-GridRepainting思路bfs找到从起点到终点的最短路,+1(起点),即为至少留下的白色块的个数则答案=总白色块数-(最短路+1)代码Code//https://atcoder.jp/contests/abc088/tasks/abc088_d#include<iostream>#include<algorithm>#include<vector>#incl......
  • 0712最短路选写
    [CF1842D]TenzingandHisAnimalFriendsDescriptionTenzing有\(n\)个朋友,每次举办聚会可以邀请一些朋友,有如下限制:\(1\)必须参加,\(n\)不能参加。有\(m\)条限制\((u,v,w)\),表示\(u\)和\(v\)中只有一个在聚会中的总时间不超过\(w\)。最大化聚会总时间,......
  • abc086d <二维前缀和 同余>
    题目D-Checker思路坐标对2k取余,通过二维前缀和计算满足条件的个数;也可对k取余,参考;代码Code//https://atcoder.jp/contests/abc086/tasks/arc089_b//二维前缀和同余#include<iostream>#include<algorithm>#include<vector>#include<cstring>usi......
  • AtCoder Beginner Contest 309 - D(最短路)
    目录D-AddOneEdge法一:dijkstra法二:BFS+队列题目传送门:abc309前面的简单题就不放了D-AddOneEdge题意:给你一个无向图图,分为两个连通块,一个顶点数为n1(1~n1),一个顶点数为n2(n1+1~n1+n2),图中共有m条边。如果现在在两个连通块之间连接一条边,那么顶点1与顶点n1+n2......
  • 2023Tsinghua-HKUSTA G <最短路 Dijkstra>
    题目G.TreasureHuntinMaze代码Code//<堆优化dijkstra>//分别从起点和终点进行dijkstra,得到i,j到起点和终点的最短距离和最短路径数,//则最短路为dis0[x][y]+dis1[x][y],最短路径数为cnt0[x][y]*cnt1[x][y]#include<iostream>#include<algorithm>#incl......
  • newcoder61132D <最短路 二分答案>
    题目松鼠回家思路对n个结点的松果个数排序,二分最大松果个数check(x),跑最短路,在不访问比x松果个数多的节点的情况下,从起点到终点消耗的最小体力代码Code#include<iostream>#include<algorithm>#include<vector>#include<cstring>#include<queue>using......
  • BOSHIDA DC电源模块短路保护的机制
    BOSHIDADC电源模块短路保护的机制DC电源模块短路保护是指在输出端短路时,电源自动保护以避免损坏。该保护机制通常包括以下几个方面:1.过流保护当输出端短路时,电源输出电流会急剧增大,如果超过电源额定电流,就会触发过流保护机制,使电源自动关闭输出。2.数字保护现代DC电源模块......
  • 电力系统三相短路故障分析simulink仿真加报告
    电力系统三相短路故障分析simulink仿真加报告ID:82200626567823070......