同余最短路
P3403 假设 \(x<y<z\) ,把 \(H--\) ,让值域变成 \([0,H]\)
1.设集合S,
\[S=\{l | l = by+cz\} \]
也就是只用 \(y\) 跟 \(z\) 能到的楼层 则对每个 \(l\) ,加上若干的 \(x\) 也是答案。 但是 \(S\) 中会有多个数与 \(x\) 同余,导致重复统计答案。 参考1
2.思考选出 \(S\) 中剩余类代表元进行答案的统计。 设 \(S\) 中模 \(x\) 为 \(k\) 的剩余类中的最小值作为代表元 \(f(k)\) 也就是说 \(S\) 被 \(mod x\) 的余数划分为多个集合,\(f(k)\) 作为余数为 \(k\) 的集合的最小值,该最小值作为代表元。
对这个群(显然)的代表元,有如下转移
\[f(k) + y \ge f((k + y) \% x) \]
先讨论y。则z同理 我们看一下+y的转移 如何证明 \(f((k+y)\%x)\) 小于等于 \(f(k) + y\) ?有
\[f(k) \ge k \]
则
\[f(k) + y \ge k + y \]
\[f((k+y) \% x) \le f(k + y) \le k + y \le f(k) + y \]
不等式链成立.
那就可以用最短路进行求解了。
何意味呢? 我又不知道 \(f\) 咋求
其实现在是差分约束 也就是说,\(0...x-1\) 这 \(x\) 个结点 他们的 \(f\) 就是他们的 \(dis\)
求出 \(f\) 之后,对每个 \(f\) 的元素统计答案
每个元素的答案是 \((H-f) / x + 1\)
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const ull maxn = 1e5 + 5;
const ull inf = ULLONG_MAX;
struct node {
ull v, w;
};
vector<node> e[maxn];
void addedge(ull u, ull v, ull w)
{
e[u].push_back({v, w});
}
vector<ull> dijkstra(ull n, ull s)
{
vector<ull> dis(n, inf);
vector<bool> vis(n, false);
struct pqnode {
ull u, dis;
bool operator < (const pqnode &b) const {
return dis > b.dis;
}
};
priority_queue<pqnode> pq;
dis[s] = 0;
pq.push({s, dis[s]});
while(!pq.empty()) {
ull u = pq.top().u;
pq.pop();
if(vis[u]) continue;
vis[u] = 1;
for(auto edge : e[u])
{
ull v = edge.v, w = edge.w;
if(dis[v] > dis[u] + w)
{
dis[v] = dis[u] + w;
pq.push({v, dis[v]});
}
}
}
return dis;
}
int main()
{
ull h, x, y, z;
cin >> h >> x >> y >> z;
h--;
for(ull k = 0; k < x; k++)
{
addedge(k, (k + y) % x, y);
addedge(k, (k + z) % x, z);
}
vector<ull> dis = dijkstra(x, 0);
ull ans = 0;
for(ull i = 0; i < x; i++)
{
// 注意S集合不一定有x的所有余数的代表元。
if(dis[i] <= h)ans += (h - dis[i]) / x + 1;
}
cout << ans;
}