luoguP5444 [APIO2019]奇怪装置 数论+贪心
对于点对 $(x,y)$ ,考虑求出其循环节.
那么有 $(x+\frac{x}{B}) \mod A=(x+kB+\frac{x+kB}{B}) \mod A$
其中 $\frac{x+kB}{B}$ 向下取整显然可以写成 $\frac{x}{B}+k$
则有 $kB+k=0(\mod A)$
解得最小的 $k$ 为 $\frac{ lcm(A,B+1)}{B+1}$,即循环节为 $\frac{lcm(A,B+1)}{B+1} \times B=\frac{A \times B}{gcd(A,B+1)}$
令 $G$ 为这个循环节.
那么如果两个 $t$ 表示的点对相同,当且仅当对 $G$ 取模得到的余数相同.
于是我们就可以将所有 $[l,r]$ 区间对 $G$ 取模映射到 $[0,G)$,然后求一下区间并集即可.
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll long long
#define N 1000009
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
int n;
ll A,B,G,L[N],R[N];
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a; }
struct line
{
ll l,r;
line(ll l=0,ll r=0):l(l),r(r){}
bool operator<(const line &b) const { return l<b.l; }
}s[N<<1];
int main()
{
// setIO("input");
int i,j,tot=0;
scanf("%d%lld%lld",&n,&A,&B);
G=A/gcd(A,B+1)*B;
for(i=1;i<=n;++i)
{
scanf("%lld%lld",&L[i],&R[i]);
if(L[i]-R[i]+1>=G) { printf("%lld\n",G); return 0; }
else
{
L[i]%=G,R[i]%=G;
if(L[i]<=R[i]) s[++tot]=line(L[i],R[i]);
else s[++tot]=line(0,R[i]),s[++tot]=line(L[i],G-1);
}
}
sort(s+1,s+1+tot);
ll ans=0;
for(i=1;i<=tot;i=j)
{
ll nr=s[i].r;
for(j=i;j<=tot&&s[j].l<=nr;++j) nr=max(nr,s[j].r);
ans+=nr-s[i].l+1;
}
printf("%lld\n",ans);
return 0;
}

浙公网安备 33010602011771号