【LOJ#3144】[APIO2019]奇怪装置(数论)
题面
题解
突然发现\(LOJ\)上有\(APIO\)的题啦,赶快来做一做。
这题是窝考场上切了的题嗷。写完暴力之后再推了推就推出正解了。。。
考虑\(t1,t2\)两个时刻,如果两个时刻的\((x,y)\)相等的话,考虑是一种什么样的情况。
\[\begin{cases}
t_1+[\frac{t_1}{B}]\equiv t_2+[\frac{t_2}{B}](\mod A)\\
t_1\equiv t_2(\mod B)
\end{cases}\]
那么根据第二个条件,我们不妨令\(t_1+kB=t_2,k>0,k\in Z\)。
那么带到第一个式子中就是:
\[t_1+[\frac{t_1}{B}]\equiv t_1+kB+k+[\frac{t1}{B}](\mod A)\]
化简之后得到
\[k(B+1)\equiv 0(\mod A)\]
而\(A,B\)都是常量,所以\(\frac{A}{gcd(A,B+1)}|k\)。令\(g=\frac{A}{gcd(A,B+1)}\),所以\(g|k\)。
所以\(k\)要是\(g\)的倍数的时候才会满足这个条件。而\(t_1\mod B\)的取值共有\(B\)种,所以不难得到循环节就是\(T=gB\)。
那么把所有\(l,r\)取模之后得到一条条的线段,线段在\([0,T)\)的覆盖区间总长度就是答案,可以很简单的差分计算出答案。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
#define ll long long
#define MAX 1000100
inline ll read()
{
ll x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
ll n,sum,l[MAX],r[MAX],A,B,d,T,ans;
multiset<pair<ll,int> >S;
#define mp make_pair
void Add(ll l,ll r){S.insert(mp(l,1));S.insert(mp(r+1,-1));}
int main()
{
n=read();A=read();B=read();d=__gcd(A,B+1);
for(int i=1;i<=n;++i)l[i]=read(),r[i]=read(),sum+=r[i]-l[i]+1;
if(1.0*A*B/d>1e18){printf("%lld\n",sum);return 0;}
T=A/d*B;
for(int i=1;i<=n;++i)
{
if(r[i]-l[i]+1>=T){printf("%lld\n",T);return 0;}
if(l[i]/T!=r[i]/T)Add(l[i]%T,T-1),Add(0,r[i]%T);
else Add(l[i]%T,r[i]%T);
}
S.insert(mp(T,0));
ll lst=-1,c=0;
for(auto a:S)
{
if(c>0)ans+=a.first-lst;
c+=a.second;
lst=a.first;
}
printf("%lld\n",ans);
return 0;
}