上星期就想写了,一直没时间……
引入
同余最短路是用来解决用 个整数,能拼凑成其他多少整数,最小能拼凑成多大整数,拼凑成一个模 意义下整数需要的最小拼凑次数。
同余最短路通过将问题每个整数上的答案转化为模 意义下的答案,在模 意义下进行计算,最终求出答案。
同余最短路
有点抽象,这里有道例题:洛谷 [P3403]
题目大意是有三个数 ,在 的基础上用这三个数凑出一个不超过 的数,求这个数的最大值。
如果定义状态 表示 能否被凑出来,那么有
但注意到 ,这样转移显然无法接受。
我们改设状态 表示对于模 值为 的数,只用 能凑出的最小值,即 。对于求出来的 ,又可以用任意个 来凑出其他数。那么我们找到最大的 ,使得 ,用 更新答案,而 ,这样做是可行的。那么就有状态转移
如何解决它呢?我们可以对于每个 建边 与 ,这样的话以结点 为起点到结点 的最短路就可表示 的值,这个很好理解,就像差分约束建边一样。于是跑一边最短路结束。
另外,一般作为模数的那个数取所有数中最小的那个,这样可以保证剩余类尽可能地小。
Code:
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define lp(i, j, n) for(int i = j; i <= n; ++i)
#define dlp(i, n, j) for(int i = n; i >= j; --i)
#define mst(n, v) memset(n, v, sizeof(n))
#define mcy(n, v) memcpy(n, v, sizeof(v))
#define INF 1e18
#define MAX4 0x3f3f3f3f
#define MAX8 0x3f3f3f3f3f3f3f3f
#define pii pair<int, int>
#define pll pair<ll, ll>
#define co(x) cerr << (x) << ' '
#define cod(x) cerr << (x) << endl
#define fi first
#define se second
#define eps 1e-8
#define lc(x) ((x) << 1)
#define rc(x) ((x) << 1 ^ 1)
#define pb(x) emplace_back(x)
using namespace std;
const int N = 100010;
namespace Gr {
static const int M = N << 1;
struct edge { int v, nxt; ll w; } E[M];
int en, hd[N];
void add(int u, int v, ll w) { E[++en] = { v, hd[u], w }, hd[u] = en; }
struct node {
int pos; ll dis;
bool operator < (const node & t) const { return dis > t.dis; }
};
ll dis[N];
bool vis[N];
priority_queue<node> q;
void dij() {
mst(dis, 0x3f);
dis[1] = 1, q.push({ 1, 1 });
while(!q.empty()) {
node fr = q.top(); q.pop();
int u = fr.pos; ll d = fr.dis;
if(vis[u]) continue;
vis[u] = 1;
for(int i = hd[u]; i; i = E[i].nxt) {
int v = E[i].v; ll w = E[i].w;
if(!vis[v] && d + w < dis[v]) dis[v] = d + w, q.push({ v, dis[v] });
}
}
}
}
using namespace Gr;
ll H, x, y, z;
signed main() {
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
#ifndef READ
ios::sync_with_stdio(false);
cin.tie(0);
#endif
cin >> H >> x >> y >> z;
if(y < x) swap(x, y);
if(z < x) swap(x, z);
if(x == 1) return cout << H << endl, 0;
lp(i, 0, x - 1) {
add(i, (i + y) % x, y);
add(i, (i + z) % x, z);
}
dij();
ll ans = 0;
lp(i, 0, x - 1) if(H >= dis[i]) ans += (H - dis[i]) / x + 1;
cout << ans << endl;
return 0;
}
还有一道题: CF [986F]
简要题意:
给定 与 ,问是否能将 分为若干个 的因数(11 除外)之和,每个因数都可以被选多次。
,,最多 50 种不同的 。
一共 组询问,。
对于质因数个数为 的情况,判断是否 整除 。
对于质因数个数为 的情况,同样用同余最短路类似的思路,设这两个数为 ,那么有 ,于是 ,算出 后,判断 是否不大于 即可。
对于质因数个数大于 的情况,可以保证每个质因数大小不超过 ,显然分解出 的质因数然后直接同余最短路,思路类似。
对于每个不同的 ,求出答案即可,分解质因数可用 Pollard_Rho 快速分解。
Code:
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define lp(i, j, n) for(int i = j; i <= n; ++i)
#define dlp(i, n, j) for(int i = n; i >= j; --i)
#define mst(n, v) memset(n, v, sizeof(n))
#define mcy(n, v) memcpy(n, v, sizeof(v))
#define INF 1e18
#define MAX4 0x3f3f3f3f
#define MAX8 0x3f3f3f3f3f3f3f3f
#define pii pair<int, int>
#define pll pair<ll, ll>
#define co(x) cerr << (x) << ' '
#define cod(x) cerr << (x) << endl
#define fi first
#define se second
#define eps 1e-8
#define lc(x) ((x) << 1)
#define rc(x) ((x) << 1 ^ 1)
#define pb(x) emplace_back(x)
using namespace std;
const int N = 60, M = 10010;
ll qmul(ll a, ll b, ll p) { return (a * b - (ll)((long double)a / p * b) * p + p) % p; }
ll qpow(ll x, ll k, ll p) {
x %= p;
ll res = 1;
while(k) {
if(k & 1) res = qmul(res, x, p);
x = qmul(x, x, p), k >>= 1;
}
return res;
}
const int pcnt = 12;
const ll p0[pcnt + 1] = { 0,2,3,5,7,11,13,17,19,61,2333,4567,24251 };
bool miller_rabin(ll x, ll p) {
if(qpow(x, p - 1, p) != 1) return false;
ll k = p - 1;
while(!(k & 1)) {
k >>= 1;
ll t = qpow(x, k, p);
if(t != 1 && t != p - 1) return false;
if(t == p - 1) return true;
}
return true;
}
bool test(ll p) {
if(p == 1) return false;
lp(i, 1, pcnt) if(p == p0[i]) return true;
lp(i, 1, pcnt) if(!miller_rabin(p0[i], p)) return false;
return true;
}
ll mgcd(ll a, ll b) { return a ? mgcd(b % a, a) : b; }
mt19937 rd(time(NULL));
vector<ll> c[N];
ll F(ll x, ll b, ll p) { return (qmul(x, x, p) + x + b) % p; }
void pollard_rho(int q, ll x) {
// co("--"), co(q), co(x), cod(test(x));
if(test(x)) return c[q].pb(x), void();
while(true) {
ll t1 = rd() % (x - 1) + 1, b = rd() % (x - 1) + 1, t2 = F(t1, b, x);
int lim = 1;
while(t1 != t2) {
ll cnt = 1;
lp(i, 1, lim) {
ll tmp = qmul(cnt, abs(t1 - t2), x);
if(!tmp) break;
cnt = tmp;
t1 = F(t1, b, x), t2 = F(F(t2, b, x), b, x);
}
ll t = mgcd(cnt, x);
if(t != 1) return pollard_rho(q, t), pollard_rho(q, x / t), void();
lim = min(lim << 1, 128);
}
}
}
void xmin(__int128 & x, __int128 & y, __int128 a, __int128 b) {
__int128 t = mgcd(a, b), t1 = b / mgcd(a, b), t2 = a / mgcd(a, b);
__int128 k = x / t1;
x %= t1;
if(x < 0) x += t1, k = k - 1;
y += t2 * k;
}
namespace Gr {
const int L = 3000010;
struct edge { int v, nxt; ll w; } E[L];
int en, hd[L];
void add(int u, int v, ll w) { E[++en] = { v, hd[u], w }, hd[u] = en; }
struct node {
int pos; ll dis;
bool operator <(const node & t) const { return dis > t.dis; }
};
ll dis[L];
bool vis[L];
priority_queue<node> q;
void dij() {
mst(dis, 0x3f), mst(vis, 0);
dis[0] = 0, q.push({ 0, 0 });
while(!q.empty()) {
node fr = q.top(); q.pop(); int u = fr.pos;
if(vis[u]) continue;
vis[u] = 1;
for(int i = hd[u]; i; i = E[i].nxt) {
int v = E[i].v; ll w = E[i].w;
if(!vis[v] && dis[u] + w < dis[v]) dis[v] = dis[u] + w, q.push({ v, dis[v] });
}
}
}
}
using namespace Gr;
struct ques { ll n, k; } qs[M];
int T;
map<ll, int> ind;
vector<ll> ans[N];
bool iscalc[N];
signed main() {
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
#ifndef READ
ios::sync_with_stdio(false);
cin.tie(0);
#endif
cin >> T;
int cnt = 0;
lp(i, 1, T) {
cin >> qs[i].n >> qs[i].k;
if(qs[i].k == 1) continue;
if(ind.find(qs[i].k) == ind.end()) {
ind[qs[i].k] = ++cnt;
pollard_rho(cnt, qs[i].k);
}
}
lp(i, 1, cnt) sort(c[i].begin(), c[i].end());
// cod(c[1].size());
// lp(i, 1, cnt) { for(auto t : c[i]) co(t); cod(""); }
lp(i, 1, T) {
if(qs[i].k == 1) { cout << "NO" << endl; continue; }
int idx = ind[qs[i].k];
if(c[idx].size() == 1) {
cout << (qs[i].n % qs[i].k == 0 ? "YES" : "NO") << endl;
}
else if(c[idx].size() == 2) {
__int128 a = c[idx][0], b = c[idx][1];
ll res = qs[i].n % a * qpow(b % a, a - 2, a) % a;
// co(x), cod(y);
cout << (res * b <= qs[i].n ? "YES" : "NO") << endl;
}
else {
if(iscalc[idx]) {
cout << (ans[idx][qs[i].n % c[idx][0]] <= qs[i].n ? "YES" : "NO") << endl;
continue;
}
mst(hd, 0), en = 0, mst(E, 0);
lp(i, 1, c[idx].size() - 1) {
lp(j, 0, c[idx][0] - 1) add(j, (j + c[idx][i]) % c[idx][0], c[idx][i]);
}
dij();
cout << (dis[qs[i].n % c[idx][0]] <= qs[i].n ? "YES" : "NO") << endl;
lp(i, 0, c[idx][0] - 1) ans[idx].pb(dis[i]);
iscalc[idx] = 1;
}
}
return 0;
}
写得有点草率,就这样吧先。