【LG5444】[APIO2019]奇怪装置

【LG5444】[APIO2019]奇怪装置

题面

洛谷

题目大意:

给定\(A,B\),对于\(\forall t\in \mathbb N\),有二元组\((x,y)=((t+\lfloor\frac tB\rfloor)\bmod A,t\bmod B)\)

对于给定的\(n\)个区间\([l,r]\),要你求出\(t\in [l_1,r_1]\bigcup [l_2,r_2]...\bigcup [l_n,r_n]\)对应有多少个不同的二元组。

数据范围:

\(1\leq n\leq 10^6,1\leq A,B\leq 10^{18},0\leq l_i\leq r_i\leq 10^{18}\)

题解

你首先要考虑到这种问题是有个循环节的 否则就会像我一样得到10分

\(t_1<t_2\)所对应的二元组完全相同,那么

\[\begin{cases} t_1+\lfloor\frac{t_1}{B} \rfloor \equiv t_2 + \lfloor \frac{t_2}{B} \rfloor(\bmod A)\\ t_1\equiv t_2(\bmod B) \end{cases} \]

那么根据第二个条件,我们不妨令$$t_1+kB=t_2,k\in \mathbb N$$

那么带到第一个式子中就是:

\[t_1+\lfloor\frac{t_1}{B}\rfloor\equiv t_1+kB+k+\lfloor\frac{t_1}{B}\rfloor(\bmod A) \]

化简得:$$k(B+1)\equiv 0(\bmod A)$$

\(\therefore \frac{A}{gcd(A,B+1)}|k\),即\(k\)最小为\(\frac{A}{gcd(A,B+1)}\)

那么循环节\(T=kB=\frac{AB}{gcd(A,B+1)}\)

然后对于所有\([l,r]\)就可以转化为线段覆盖了,想怎么维护都行。

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring> 
#include <cmath> 
#include <algorithm>
#include <set> 
using namespace std;
typedef long long ll; 
inline ll gi() {
    register ll data = 0, w = 1; 
    register char ch = 0;
    while (!isdigit(ch) && ch != '-') ch = getchar(); 
    if (ch == '-') w = -1, ch = getchar(); 
    while (isdigit(ch)) data = 10 * data + ch - '0', ch = getchar(); 
    return w * data; 
} 
const int MAX_N = 1e6 + 5; 
ll N, A, B, l[MAX_N], r[MAX_N], T, ans; 
multiset<pair<ll, int> > st; 
void Add(ll l, ll r) { st.insert(make_pair(l, 1)), st.insert(make_pair(r + 1, -1)); } 
int main () {
#ifndef ONLINE_JUDGE 
    freopen("cpp.in", "r", stdin); 
#endif 
	N = gi(), A = gi(), B = gi(); ll d = __gcd(A, B + 1), sum = 0; 
	for (int i = 1; i <= N; i++) l[i] = gi(), r[i] = gi(), sum += r[i] - l[i] + 1; 
	if (1.0 * A / d * B > 1e18) return printf("%lld\n", sum) & 0; 
	T = A / d * B; 
	for (int i = 1; i <= N; i++) { 
		if (r[i] - l[i] + 1 >= T) return printf("%lld\n", T) & 0; 
		l[i] %= T, r[i] %= T; 
		if (l[i] > r[i]) Add(l[i], T - 1), Add(0, r[i]); 
		else Add(l[i], r[i]); 
	} 
	st.insert(make_pair(T, 0)); 
	ll lst = -1, c = 0; 
	for (auto it : st) { 
		if (c > 0) ans += it.first - lst; 
		c += it.second, lst = it.first; 
	} 
	printf("%lld\n", ans); 
    return 0; 
} 
posted @ 2019-11-04 14:25  heyujun  阅读(210)  评论(0编辑  收藏  举报