同余最短路

同余最短路其实是一种优化最短路建图的方法。

通常是解决给定m个整数,求这m个整数能拼凑出多少的其他整数(这m个整数可以重复取)或给定m个整数,求这m个整数不能拼凑出的最小(最大)的整数。

我们通过一道例题来讲解。

P3403 跳楼机

简化一下题意:用a,b,c(这里用a,b,c来代替x,y,z)三个数能组成几个小于h的整数。$h \leq 2^{63}-1$

因为h过大所以直接建图显然是不行的,我们要优化空间。

我们因为这个跳的顺序是无关的,所以每个数都可以由若干次b/c再加上若干次a而形成的。

根据带余除法我们知道所有的整数数都可以写成ax+r的形式,其中a是除数,x是商而r是余数。

我们求出通过b/c操作能到达的最小的mod a余数是r的数,然后用一些算法即可求出能到达多少小于h的整数(到时再讲)。

这时我们同余最短路就该排上用场了。这个最小即可表示成最短路。

我们可以让a来做这个除数(其实应该用最小的最优),则r属于$[0,a-1]$。

我们要求出所有到达所有r的最小值。所以对于每个r建立一个点。

它可以通过b,c到其它的数(点),所以我们对于每个点u连一条到v=(u+(b/c))%a的边,长度为(b/c)。

现在从0开始跑最短路即可(初始化dis[0]=0)。

设余数r的最短路为dis[r],则可以到$\frac{h-dis[r]}{a}+1$个整数,统计答案。

#include <bits/stdc++.h>
using namespace std;
const long long MAXA = 1e5 + 10;
struct node{
    long long pre, to, val;
}edge[MAXA * 20];
long long head[MAXA], tot;
long long n, h;
long long a[20];
long long dis[MAXA], vis[MAXA];
queue<long long> q;
void add(long long u, long long v, long long l) {
    edge[++tot] = node{head[u], v, l};
    head[u] = tot;
}
void spfa() {
    memset(dis, 0x3f, sizeof(dis));
    dis[0] = 0;
    vis[0] = 1;
    q.push(0);
    while (!q.empty()) {
        long long x = q.front(); q.pop();
        for (long long i = head[x]; i; i = edge[i].pre) {
            long long y = edge[i].to;
            if (dis[y] > dis[x] + edge[i].val) {
                dis[y] = dis[x] + edge[i].val;
                if (!vis[y]) {
                    vis[y] = 1;
                    q.push(y);
                }
            }
        }
        vis[x] = 0;
    }
}
long long solve(long long x) {
    long long ret = 0;
    for (long long i = 0; i < a[1]; i++) {
        if (dis[i] <= x) {
            ret += (x - dis[i]) / a[1] + 1;
        }
    }
    return ret;
}
int main() {
    n = 3;
    cin >> h;
    for (long long i = 1; i <= n; i++) {
        cin >> a[i];
    }
    for (long long i = 0; i < a[1]; i++) {
        for (long long j = 2; j <= n; j++) {
            add(i, (i + a[j]) % a[1], a[j]);
        }
    }
    spfa();
    cout << solve(h - 1);//他刚开始在1楼所以要-1
    return 0;
}

习题:

[国家集训队]墨墨的等式

原文地址:https://www.cnblogs.com/zcr-blog/p/12631741.html

时间: 2024-08-29 09:50:51

同余最短路的相关文章

【66测试20161115】【树】【DP_LIS】【SPFA】【同余最短路】【递推】【矩阵快速幂】

还有3天,今天考试又崩了.状态还没有调整过来... 第一题:小L的二叉树 勤奋又善于思考的小L接触了信息学竞赛,开始的学习十分顺利.但是,小L对数据结构的掌握实在十分渣渣.所以,小L当时卡在了二叉树. 在计算机科学中,二叉树是每个结点最多有两个子结点的有序树.通常子结点被称作“左孩子”和“右孩子”.二叉树被用作二叉搜索树和二叉堆.随后他又和他人讨论起了二叉搜索树.什么是二叉搜索树呢?二叉搜索树首先是一棵二叉树.设key[p]表示结点p上的数值.对于其中的每个结点p,若其存在左孩子lch,则key

HDU 6071 Lazy Running (同余最短路 dij)

Lazy Running Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)Total Submission(s): 1384    Accepted Submission(s): 597 Problem Description In HDU, you have to run along the campus for 24 times, or you will fail in

Luogu P3403 跳楼机|同余最短路

题意:给出跳楼机的4个操作,分别为 1.向上移动\(x\)层: 2.向上移动\(y\)层: 3.向上移动\(z\)层: 4.回到第一层. 显然,并不需要 求从第一层开始,能到达\(1\)到\(h\)中的多少层? \(1<=h<=2^{63}-1\) \(1<=x, y, z<=100000\) 题解: 好像可以直接\(DP\)? 布星啊,看下数据范围. 那先来推推定理? 接下来假设\(x\le y\le z\) 对于一个数\(k\),若它能到达,则\(k+x,k+2x,k+...\

HDU 6071 Lazy Running (同余最短路)

Lazy Running Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)Total Submission(s): 101    Accepted Submission(s): 40 Problem Description In HDU, you have to run along the campus for 24 times, or you will fail in PE

51nod 1624 取余最短路(set)

题意: 佳佳有一个n*m的带权矩阵,她想从(1,1)出发走到(n,m)且只能往右往下移动,她能得到的娱乐值为所经过的位置的权的总和. 有一天,她被下了恶毒的诅咒,这个诅咒的作用是将她的娱乐值变为对p取模后的值,这让佳佳十分的不开心,因为她无法找到一条能使她得到最大娱乐值的路径了! 她发现这个问题实在是太困难了,既然这样,那就只在3*n的矩阵内进行游戏吧! 现在的问题是,在一个3*n的带权矩阵中,从(1,1)走到(3,n),只能往右往下移动,问在模p意义下的移动过程中的权总和最大是多少. 实际上路

HDU 6071 同余最短路 spfa

Lazy Running Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)Total Submission(s): 657    Accepted Submission(s): 284 Problem Description In HDU, you have to run along the campus for 24 times, or you will fail in P

hdu 6071 Lazy Running(同余最短路)

题目链接:hdu 6071 Lazy Running 题意: 给你4个点,每两个相邻点有一个距离,现在让你在这四个点来回跑步,从2开始,最后回到2,问你找一个距离ans,ans>=k,问最小的ans是多少. 题解: Claris的官方题解: 1 #include<bits/stdc++.h> 2 #define F(i,a,b) for(int i=(a);i<=(b);++i) 3 using namespace std; 4 using ll=long long; 5 6 co

51nod_1459 最短路 dijkstra 特调参数

好多基础知识都没补完,只好看到.用到一个赶紧补全一个,并且保证下次需要的时候直接用,不用回来再补: 其实这个算法是在补同余最短路的时候用到的,当时突然发现理解算法导论上的原理甚至有效性证明,但是就是没办法写出来合适的代码..于是到处寻找可以用来试验最短路径算法的试验场(当时学矩阵快速米的时候也是找了51nod上面的一到基础题作为测试的)来熟悉和不全最短路相关基础知识. 首先是DIJKSTRA 对于DIJKSTRA首先是个贪心算法,每次从集合中选择一条没有走过的最短路径来作为新的边,到达新的节点.

BZOJ 2118 墨墨的等式(最短路)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2118 [题目大意] 求a1x1+a2y2+…+anxn=B在B的取值范围,有多少B可以使等式存在非负整数解. [题解] 同余最短路,不等式解集计数即可. [代码] #include <cstdio> #include <algorithm> #include <queue> using namespace std; const int N=500010; n