题解 P5444 【[APIO2019]奇怪装置】
先来证明一个引理。
那么又
即
正文开始。
我们想办法计算出
当
把
则
此时因为
那么
即
不难想出和
这时根据引理,把
得到
所以最小循环节
做到这里恭喜你做完了这道题的一半。
我们考虑把这道题变成一道区间覆盖问题。
对区间
情况1:若
则说明此区间的长度已经大于了循环节,即循环节的每一个数都能取到,答案就是循环节,直接退出程序即可。
情况2:
将
若
表示这段区间被完全包含在了循环节里面,即这条线段都可以取到。
我们在数轴上画一条
情况三:
接着情况2,讨论
此时的情况是
我们在数轴上画
最后的答案就是数轴被覆盖的长度。
算这个东西就很简单了。
把所有线段按照左端点排序。
我们设
之后枚举每
否则说明这条线段也可以和当前已经连通的线段相连。
我们
代码
#include <bits/stdc++.h>
const int maxn = 1e6 + 10;
typedef long long ll;
template<class t> inline void read(t& res) {
res = 0; char ch = getchar(); bool neg = 0;
while (!isdigit(ch))
neg |= ch == '-', ch = getchar();
while (isdigit(ch))
res = (res << 1) + (res << 3) + (ch & 15), ch = getchar();
if (neg)
res = -res;
}
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll n, m, i, j, k, A, B, sz, len, nl, nr;
ll ans;
struct line {
ll l, r;
line() { l = r = 0; }
line(ll _l, ll _r) { l = _l; r = _r; }
inline friend bool operator < (line a, line b) {
return a.l < b.l;
}
} l[maxn << 1];
int main() {
read(n); read(A); read(B);
len = A / gcd(A, B + 1) * B;
for (int i = 1; i <= n; i++) {
ll le, ri; read(le); read(ri);
if (ri - le + 1 >= len) { printf("%lld\n", len); return 0; }
le %= len;
ri %= len;
if (le <= ri)
l[++sz] = line(le, ri);
else
l[++sz] = line(le, len - 1),
l[++sz] = line(0, ri);
}
std::sort(l + 1, l + sz + 1);
l[++sz] = line(len + 1, 0);
nl = l[1].l, nr = l[1].r;
for (int i = 2; i <= sz; i++) {
if (nr < l[i].l) {
ans += nr - nl + 1;
nl = l[i].l;
nr = l[i].r;
} else {
nr = std::max(nr, l[i].r);
}
}
printf("%lld\n", ans);
return 0;
}