Skip to content

Commit 0845ff4

Browse files
committed
add min_of_mod_of_linear
1 parent e67ee1c commit 0845ff4

File tree

2 files changed

+126
-0
lines changed

2 files changed

+126
-0
lines changed

number/min_of_mod_of_linear.hpp

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#pragma once
2+
3+
#include <cassert>
4+
#include <vector>
5+
#include <utility>
6+
7+
// min((ax + b) mod m) for 0 <= x < n
8+
// 0 < n, 0 < m <= 1e9
9+
// https://maspypy.com/library-checker-min-of-mod-of-linear
10+
template <class Int = int, class Long = long long>
11+
auto min_of_mod_of_linear(Int n, const Int &m, Int a, Int b) {
12+
13+
// p = (xs, ys), v = (dx, dy) として
14+
// 列 (p, p + v, ..., p + (len - 1)v) が点群全体を包絡する
15+
struct Result {
16+
Int xs, ys;
17+
Int dx, dy;
18+
Int len;
19+
20+
Int min() const { return ys + dy * (len - 1); }
21+
};
22+
23+
assert(0 < n);
24+
assert(0 < m);
25+
26+
if (n > m) n = m;
27+
a = (m - a % m) % m;
28+
b = (b % m + m) % m;
29+
30+
Int x = 0, y = b;
31+
32+
Int p = 0, q = 1, r = 1, s = 0; // p/q <= a/m < r/s
33+
34+
std::vector<Result> ret{Result{x, y, 0, 0, 1}};
35+
36+
while (true) {
37+
if (x + q > n - 1) break;
38+
39+
const Long aqmp = (Long)a * q - (Long)m * p;
40+
if (aqmp == 0) break; // p/q == a/m
41+
42+
const Long mras = (Long)m * r - (Long)a * s;
43+
44+
assert(aqmp > 0);
45+
assert(mras > 0);
46+
47+
if (const Long num = p + r, den = q + s; num * m <= den * a) { // num/den <= a/m 下側を更新
48+
49+
// q + is <= n - 1 - x
50+
51+
// (p + ir) / (q + is) <= a/m (下側である条件) i >= 0
52+
// <=> m(p + ir) <= a(q + is) <=> (mr - as)i <= aq - mp
53+
54+
// a(q + is) - m(p + ir) <= Y (改善する条件) i >= 0
55+
// (mr - as)i >= aq - mp - Y
56+
57+
const Long iub = aqmp / mras;
58+
59+
const Long imp_i_lb = std::max<Long>((std::max<Long>(aqmp - y, 0) + mras - 1) / mras, 0);
60+
61+
if (imp_i_lb <= iub) {
62+
p += imp_i_lb * r;
63+
q += imp_i_lb * s;
64+
65+
const Long c = (Long)a * q - (Long)m * p;
66+
assert(0 <= c);
67+
assert(c <= y);
68+
69+
const Long kub = y / c;
70+
const Long k = std::min<Long>(kub, (n - 1 - x) / q);
71+
72+
if (k) ret.push_back(Result{x + q, (Int)(y - c), q, (Int)-c, (Int)k});
73+
74+
x += q * k;
75+
y -= c * k;
76+
if (k < kub) break;
77+
continue;
78+
} else {
79+
p += iub * r;
80+
q += iub * s;
81+
}
82+
} else { // num/den > a/m 上側を更新
83+
// (r + ip)/(s + iq) > a/m <=> m(r + ip) > a(s + iq) <=> (aq - mp)i < mr - as
84+
// -> i step 進んでもまだ超えない
85+
86+
const Long i = (mras - 1) / aqmp;
87+
assert(i > 0);
88+
89+
// x + (s + iq) <= n - 1
90+
// const Long iub = (n - 1 - q - x - s) / q;
91+
// if (i > iub) break;
92+
93+
r += (i - 1) * p; // NOTE: overflow
94+
s += (i - 1) * q;
95+
96+
// まだ a/m < (p + r) / (q + s)
97+
assert((Long)a * (q + s) < (Long)m * (p + r));
98+
99+
r += p;
100+
s += q;
101+
102+
// 逆転
103+
assert((Long)a * (q + s) >= (Long)m * (p + r));
104+
}
105+
}
106+
107+
// return y;
108+
return ret;
109+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#define PROBLEM "https://judge.yosupo.jp/problem/min_of_mod_of_linear"
2+
3+
#include "../min_of_mod_of_linear.hpp"
4+
#include <iostream>
5+
using namespace std;
6+
7+
int main() {
8+
cin.tie(nullptr), ios::sync_with_stdio(false);
9+
10+
int T;
11+
cin >> T;
12+
while (T--) {
13+
int n, m, a, b;
14+
cin >> n >> m >> a >> b;
15+
cout << min_of_mod_of_linear<int, long long>(n, m, a, b).back().min() << '\n';
16+
}
17+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy