ARC127F ±AB 解题报告:
题意
给定 \(a,b\)(保证互素),有一个变量 \(x\) 初始为 \(V\),你可以执行以下操作:
你只需保证操作时 \(0\leqslant x\leqslant m\),求你能得到多少个不同的 \(x\)。
\(q\) 次询问。
\(1\leqslant q\leqslant 10^5,1\leqslant a,b,V\leqslant m\leqslant 10^9\)。
分析
模拟赛场上两个人做出来了,其中有一个才新初二,只能说广二真恐怖。
先尝试利用裴蜀定理遍历每一个数,若区间长度大于等于 \(a+b\),我们可以持续进行 \(+a,-b\) 操作(两种操作一定有一个能进行),容易发现可以遍历每一个数字。
否则 \(a+b>m+1\)。
若第一步为 \(+a\),那么下一步不能为 \(+b\),也不能为 \(-a\)(没有意义),而 \(+a,-b\) 中最多只有一种合法,于是后面的方案已然确定。
第一步为 \(+b\) 时同理,我们观察这两条链可以得到两条性质:
- 链不存在循环,因为最小循环节 \(a+b>m+1\)。
- 两条链不会相交,显然。
于是我们只需计算“不断进行 \(+a,-b\) 操作,能执行多少次”。
其等价于找到最小的 \(k\) 使得 \((V+ak)\bmod b>m-a\)。
记 \(V_0=V\bmod b\),特判 \(V_0+a>m\) 的情况。
现给出如下结论:
只需说明 \(V_0+(ak\bmod b)<b\)。
若 \(V_0+(ak\bmod b)\geqslant b\),则有 \(V_0+a+(ak\bmod b-b)\leqslant V_0+a\leqslant m\)。我们可以到达 \(V_0+(ak\bmod b-b)\) 后进行一次 \(+a\) 操作,矛盾。
于是问题变为找到最小的 \(k\) 使得满足:
这是经典的类欧几里得算法,我们将其泛化为“找到最小的 \(k\) 满足 \(ak\bmod b\in[L,R]\)”。
令 \(b=ap+q,t=\lfloor\frac{ak}{b}\rfloor\),则有:
此时问题由 \((a,b)\) 转化为了 \((b\bmod a,a)\),只需递归 \(\log\) 层即可使得 \(a=0\),直接计算即可。
复杂度 \(O(q\log m)\)。
代码
#include<stdio.h>
int T,a,b,v,m;
int Euclid(int a,int b,int l,int r){
if(a==0)
return 0;
a%=b;
if((l-1)/a!=r/a)
return (l+a-1)/a;
int t=Euclid(b%a,a,(a-r%a)%a,(a-l%a)%a);
return (1ll*t*b+l+a-1)/a;
}
int calc(int a,int b,int v,int m){
int v0=v%b;
if(v0+a>m)
return v/b;
int k=Euclid(a,b,m-a-v0+1,b-v0-1);
return k+(v+1ll*a*k)/b;
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%d%d%d",&a,&b,&v,&m);
if(a+b<=m+1)
printf("%d\n",m+1);
else printf("%d\n",calc(a,b,v,m)+calc(b,a,v,m)+1);
}
return 0;
}