#3144. 「APIO 2019」奇怪装置
题目描述
考古学家发现古代文明留下了一种奇怪的装置。该装置包含两个屏幕,分别显示两个整数 (x) 和 (y)。
经过研究,科学家对该装置得出了一个结论:该装置是一个特殊的时钟,它从过去的某个时间点开始测量经过的时刻数 (t),但该装置的创造者却将 (t) 用奇怪的方式显示出来。若从该装置开始测量到现在所经过的时刻数为 (t),装置会显示两个整数:(x = ((t + lfloor frac{t}{B} floor) mod A)),与 (y = (t mod B))。这里 (lfloor x floor) 是下取整函数,表示小于或等于 (x) 的最大整数。
考古学家通过进一步研究还发现,该装置的屏幕无法一直工作。实际上,该装置的屏幕只在 (n) 个连续的时间区间段中能正常工作。第 (i) 个时间段从时刻 (l_i) 到时刻 (r_i)。现在科学家想要知道有多少个不同的数对 ((x, y)) 能够在该装置工作时被显示出来。
两个数对 ((x_1, y_1)) 和 ((x_2, y_2)) 不同当且仅当 (x_1 ot = x_2) 或 (y_1 ot = y_2)。
输入格式
第一行包含三个整数 (n, A) 与 (B)。
接下来 (n) 行每行两个整数 (l_i, r_i),表示装置可以工作的第 (i) 个时间区间。
输出格式
输出一行一个整数表示问题的答案。
数据范围与提示
对于全部数据,(1le nle 10^6,1le A,Ble 10^{18},0le l_ile r_ile 10^{18},r_i<l_{i+1})。
首先这玩意肯定是有环的。找到过后将所有线段平移到环内就可以直接做线段覆盖。
对于一个数(t),首先跟他同构的数可以表示为(t+k*B),因为要保证(y)相同。然后(t)每增加(B),(x)就增加(B+1),增加了(frac{A}{gcd(A,B+1)})后有会同构。所以环大小(frac{B*A}{gcd(A,B+1)})。
代码:
#include<bits/stdc++.h>
#define ll long long
#define N 1000005
using namespace std;
inline ll Get() {ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
int n;
ll A,B,len;
struct node {
ll l,r;
bool operator <(const node &a)const {
if(l!=a.l) return l<a.l;
return r<a.r;
}
}s[N<<1];
ll gcd(ll a,ll b) {return !b?a:gcd(b,a%b);}
int main() {
n=Get(),A=Get(),B=Get();
for(int i=1;i<=n;i++) s[i].l=Get(),s[i].r=Get();
ll g=gcd(A,B+1);
ll ans=0;
if((long double)A/g*B>1e18) {
for(int i=1;i<=n;i++) ans+=s[i].r-s[i].l+1;
} else {
ll len=A/g*B;
int tot=n;
for(int i=1;i<=n;i++) {
if(s[i].r-s[i].l+1>=len) {
cout<<len;
return 0;
}
s[i].l%=len,s[i].r%=len;
if(s[i].l>s[i].r) {
s[++tot].l=0,s[tot].r=s[i].r;
s[i].r=len-1;
}
}
sort(s+1,s+1+tot);
ll last=-1;
for(int i=1;i<=tot;i++) {
if(s[i].r<last) continue ;
ans+=s[i].r-max(s[i].l-1,last);
last=s[i].r;
}
}
cout<<ans;
return 0;
}