[ARC127F] ±AB
给定整数 \(a,b,v,m\),保证 \(a\perp b\).
初始有一个数 \(x=v\),可以不断令其加上或减去 \(a\) 或 \(b\).
过程中必须有 \(x\in[0,m]\),问 \(x\) 有多少种可能的取值。
多测。\(T\le 10^5\),\(1\le a<b\le m\le 10^9\),\(0\le v\le m\).
由于 \(a\perp b\),当 \(m\) 极大时答案一定为 \(m+1\),考虑这个下界。
当 \(m\ge a+b-1\) 时,答案为 \(m+1\).
不妨假设要进行 \(x\) 次 \(+a\),\(y\) 次 \(-b\),\(x,y\) 异号。\(a,b\) 互换同理。
由于当前的数可以取到模 \(b\) 的完全剩余系。当不能 \(-b\) 时 \(+a\) 一定有余地,所以 \(m=a+b-1\) 是最小的符合的答案。感性理解。
此时 \(m\le a+b-2\),可以得到两种操作序列:
-
能 \(+a\) 则 \(+a\),能 \(-b\) 则 \(-b\).
-
能 \(+b\) 则 \(+b\),能 \(-a\) 则 \(-a\).
一种操作不会经过重复的数。
若出现环,则存在 \(xa+yb=0\),环的大小至少为 \(a+b\).
与 \(m\le a+b-2\) 矛盾。
两种操作序列经过的数不重合。
显然,如果两者出现重合,则一定有环。
以 \(+a,-b\) 为例。
若走了 \(n\) 次 \(+a\),则走了 \(\lfloor\frac{v+na}{b}\rfloor\) 次 \(-b\),此时有
其对答案的贡献为 \(n+\lfloor\frac{v+na}{b}\rfloor\).
考虑 \(v+na\in[qb,(q+1)b)\),有
左部分为 \(\lfloor\frac{qb-v-a+m}{a}\rfloor+1=\lfloor\frac{qb-v+m}{a}\rfloor\).
如果左式 \(>\) 右式,两者的差值一定为 \(1\).
于是可以用类欧二分最小的 \(n\),即枚举 \(q'\),判断下式是否为正:
左半部分的 \(f\) 中 \(b\) 值可能为负,不妨把后面的系数扔进左边:
二分出 \(q'\),此时 \(n=\lfloor\frac{q'b-v-a+m+1}{a}\rfloor\) 或者 \(\lfloor\frac{q'b+b-v-1}{a}\rfloor\).
时间复杂度单次 \(O(\log^2\max(a,b))\).
加个 \(n=0\) 的剪枝。快飞了。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int read(){
int x=0,w=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*w;
}
ll f(ll a,ll b,ll c,ll n){
if(!n)return b/c;
if(!a)return (n+1)*(b/c);
if(a>=c||b>=c)return n*(n+1)/2*(a/c)+(n+1)*(b/c)+f(a%c,b%c,c,n);
ll M=(a*n+b)/c;
return n*M-f(c,c-b-1,a,M-1);
}
ll v,m;
ll calc(ll a,ll b){
ll l=0,r=m,mid,now=0;
while(l<=r){
mid=(l+r)>>1;
if(f(b,a+b-v-1,a,mid)-f(b,m-v,a,mid)>0)r=mid-1,now=mid;
else l=mid+1;
}
ll n=(now*b+b-v-1)/a;
return n+(v+n*a)/b;
}
ll a,b;
void solve(){
a=read(),b=read(),v=read(),m=read();
if(m>=a+b-1)return printf("%lld\n",m+1),void();
printf("%lld\n",calc(a,b)+calc(b,a)+1);
}
int main(){
int T=read();
while(T--)solve();
return 0;
}