[ARC127F] ±AB

[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\),此时有

\[(v+na)\bmod b+a\ge m+1 \]

其对答案的贡献为 \(n+\lfloor\frac{v+na}{b}\rfloor\).

考虑 \(v+na\in[qb,(q+1)b)\),有

\[\begin{cases}v+na-qb+a\ge m+1\\ v+na-qb\le b-1\end{cases} \]

\[\lceil\frac{qb-v-a+m+1}{a}\rceil\le n\le \lfloor\frac{qb+b-v-1}{a}\rfloor \]

左部分为 \(\lfloor\frac{qb-v-a+m}{a}\rfloor+1=\lfloor\frac{qb-v+m}{a}\rfloor\).

如果左式 \(>\) 右式,两者的差值一定为 \(1\).


于是可以用类欧二分最小的 \(n\),即枚举 \(q'\),判断下式是否为正:

\[\sum_{i=0}^{q'}(\lfloor\frac{bi+b-v-1}{a}\rfloor-\lfloor\frac{bi-v+m}{a}\rfloor)+q'+1 \]

\[=f(b,b-v-1,a,q')-f(b,m-v,a,q')+q'+1 \]

左半部分的 \(f\)\(b\) 值可能为负,不妨把后面的系数扔进左边:

\[=f(b,a+b-v-1,a,q')-f(b,m-v,a,q') \]

二分出 \(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;
}
posted @ 2023-10-18 22:15  SError  阅读(9)  评论(0编辑  收藏  举报