Chgtaxihe's blog Chgtaxihe's blog
首页
练习
算法学习
读书笔记
小玩意

Chgtaxihe

首页
练习
算法学习
读书笔记
小玩意
  • 数论

    • 一些小问题
    • 二次剩余学习笔记
    • 卡特兰数学习笔记
    • 同余最短路学习笔记
      • BZOJ 2118 墨墨的等式
      • 牛客练习赛60 D 斩杀线计算大师
      • 洛谷P3403 AC代码
      • 牛客练习赛60 D 斩杀线计算大师 AC代码
    • 康托展开学习笔记
    • 快速傅里叶变换学习笔记
    • 拉格朗日插值学习笔记
    • 拓展欧几里得学习笔记
    • 斐波那契数列学习笔记
    • 斯特林数笔记
    • 本原勾股数组学习笔记
    • 模运算学习笔记
    • 欧拉函数
    • 类欧几里得学习笔记
    • 素性检测与大数分解
    • 素数计数问题
    • 组合数问题
    • 高斯整数学习笔记
  • 动态规划

  • 暴力

  • 图论

  • 数据结构

  • 字符串

  • 几何

  • 博弈论

  • 其他

同余最短路学习笔记

# 前言

最短路的奇怪用法

# 例题引入 洛谷P3403 (opens new window)

AC代码

可以转化成背包大小为2632^{63}263的完全背包问题,保证不会TLE~~

题解:

参考题解 (opens new window)

设三个操作分别可以上升a,b,ca, b, ca,b,c层且a≤b≤ca\le b \le ca≤b≤c

设dist[i]dist[i]dist[i]为∃x1,x2,k=bx1+cx2(kmoda=i)\exists x_1,x_2, k=bx_1 + cx_2(k \mod a = i)∃x1​,x2​,k=bx1​+cx2​(kmoda=i)的最小整数kkk,即仅通过操作b/cb/cb/c能够到达的**对aaa取余等于iii**的最低楼层

对于第kkk层(kmoda=ik \mod a = ikmoda=i),若dist[i]≠−1dist[i] \ne -1dist[i]=−1,即iii可达,则定有i+ma(m≥0)i+ma \ (m\ge0)i+ma(m≥0)可达

因此只需计算dist[k]=min(dist[(k−b+a)%a]+b,dist[(k−c+a)%a]+c)dist[k]=min(dist[(k-b+a)\%a]+b, dist[(k-c+a)\%a]+c)dist[k]=min(dist[(k−b+a)%a]+b,dist[(k−c+a)%a]+c)

那么答案就可以转化为

ans=∑0≤i≤a⌊h−dist[i]a⌋+1ans=\sum_{0\le i \le a}{\lfloor \frac{h-dist[i]}{a} \rfloor + 1}ans=∑0≤i≤a​⌊ah−dist[i]​⌋+1

也可以看成是一条有向边edge(i,(i+b)%a)edge(i, (i+b)\%a)edge(i,(i+b)%a)边权w=bw=bw=b,ccc同理,转化后跑一遍最短路即可

# 更多题目

# BZOJ 2118 墨墨的等式

悲报:BZOJ倒了

# 牛客练习赛60 D 斩杀线计算大师

给定a,b,c,ka,b,c,ka,b,c,k,求ax+by+cz=kax+by+cz=kax+by+cz=k的一组非负整数解(保证有解)。

1≤a,b,c≤1e51\le a,b,c\le 1e51≤a,b,c≤1e5

0≤k≤1e120\le k \le 1e120≤k≤1e12


这题正解是拓展欧几里得exgcd,但是由于给了1秒时限,且mix(a,b,c)≤1e5mix(a,b,c)\le1e5mix(a,b,c)≤1e5,故可以用同余最短路做。

exgcd做法耗时4ms,同余最短路6ms,暴力500ms

AC代码

# 附录

# 洛谷P3403 AC代码

#include <bits/stdc++.h>
#define ll long long
#define pii pair<ll, int> 

using namespace std;

const ll inf = 0x3f3f3f3f3f3f3f3f;
const int maxn = 1e5 + 1000;

ll dist[maxn];

ll h, x, y, z;

void dij(ll s){
    dist[s] = s;
    priority_queue<pii, vector<pii>, greater<pii> >que;
    que.push({dist[s], s});
    while(!que.empty()){
        ll d = que.top().first, u = que.top().second;
        que.pop();
        if(d != dist[u] || d > h) continue; // 不超过h
        if(dist[(u+y)%x] > dist[u]+y){
            dist[(u+y)%x] = dist[u]+y;
            que.push({dist[(u+y)%x], (u+y)%x});
        }
        if(dist[(u+z)%x] > dist[u]+z){
            dist[(u+z)%x] = dist[u]+z;
            que.push({dist[(u+z)%x], (u+z)%x});
        }
    }
}

int main(){
    cin >> h >> x >> y >> z;
    if(x > y) swap(x, y);
    if(x > z) swap(x, z);
    if(x == 1) {cout << h << endl; return;}
    memset(dist, 0x3f, sizeof dist);
    dij(1);
    ll ans = 0;
    for(int i=0; i<x; i++){
        if(dist[i] > h) continue;
        ans += (h - dist[i])/x + 1;
    }
    cout << ans << endl;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

# 牛客练习赛60 D 斩杀线计算大师 AC代码

#include <bits/stdc++.h>
#define ll long long
#define pii pair<ll, int>  

using namespace std;

const int maxn = 1e5 + 100;

pii config[maxn];
ll dist[maxn], ans[4]={0}, idx[4];
ll a, b, c, k, rem;

bool dij(int target){
	memset(dist, 0x3f, sizeof dist);
	dist[0] = 0;
	priority_queue<pii, vector<pii>, greater<pii> > que;
	que.push({0, 0});
	while(!que.empty()){
		pii info = que.top();
		int u = info.second, tmp;
		que.pop();
		if(u == target) return true;
		if(info.first > dist[info.second]) continue;

		if(dist[tmp=(u+b)%a] > dist[u] + b){
			dist[tmp] = dist[u] + b;
			config[tmp] = {config[u].first + 1, config[u].second};
			que.push({dist[tmp], tmp});
		}

		if(dist[tmp=(u+c)%a] > dist[u] + c){
			dist[tmp] = dist[u] + c;
			config[tmp] = {config[u].first, config[u].second + 1};
			que.push({dist[tmp], tmp});
		}
	}
	return false;
}

signed main(){
	cin >> a >> b >> c >> k;
	for(int i=0; i<4; i++) idx[i] = i;
	if(a > b) swap(a, b), swap(idx[0], idx[1]);
	if(a > c) swap(a, c), swap(idx[0], idx[2]);
	rem = k % a;
	dij(rem);
	ll sum = config[rem].first * b + config[rem].second * c;
	ans[idx[0]] = (k-sum)/a, ans[idx[1]] = config[rem].first, ans[idx[2]] = config[rem].second;
    cout << ans[0] << " " << ans[1] << " " << ans[2] << endl;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
上次更新: 2021/02/24, 03:37:30
卡特兰数学习笔记
康托展开学习笔记

← 卡特兰数学习笔记 康托展开学习笔记→

最近更新
01
深入理解Java虚拟机
03-04
02
DNS工作原理
02-27
03
改用VuePress啦
02-23
更多文章>
Theme by Vdoing | Copyright © 2019-2021
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式