CF1182F Maximum Sine
最大化 \(f(x)=\left|\sin\left(\frac{p}{q}\pi x\right)\right|\) 就是让 \(\frac{px}{q}\) 越接近 \(\frac{1}{2}+k\),即 \(\frac{px}{q}\) 的小数部分 \(\frac{px \bmod q}{q}\) 越接近 \(\frac{1}{2}\)。
转化后为使 \(|2px \bmod 2q -q|\) 最小,二分该值 \(v\),设 \(l=q-v,r=q+v\),得判定 \(2px \bmod 2q\) 是否在区间 \([l,r]\) 内,有个结论:
\[\large [2px \bmod 2q \in[l,r]]=\left\lfloor \frac{2px-l}{2q} \right\rfloor-\left\lfloor \frac{2px-r-1}{2q} \right\rfloor
\]
因此,得:
\[\large \exist x \in [a,b],2px \bmod 2q \in[l,r] \Leftrightarrow \sum_{x=a}^b \left( \left\lfloor \frac{2px-l}{2q} \right\rfloor-\left\lfloor \frac{2px-r-1}{2q} \right\rfloor \right)>0
\]
和式用类欧计算,二分后用扩欧解同余方程即可求出 \(x\)。
#include<bits/stdc++.h>
#define inf 1000000000000000000
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int T;
ll a,b,p,q,l,r,ans;
ll f(ll n,ll a,ll b,ll c)
{
if(n<0) return 0;
if(!a) return (n+1)*(b/c);
if(a>=c||b>=c) return (n+1)*n/2*(a/c)+(n+1)*(b/c)+f(n,a%c,b%c,c);
ll m=(n*a+b)/c;
return n*m-f(m-1,c,c-b-1,a);
}
bool check(ll v)
{
ll l=q/2-v,r=q/2+v,v1,v2;
return f(b,p,q-l,q)-f(a-1,p,q-l,q)-f(b,p,q-r-1,q)+f(a-1,p,q-r-1,q);
}
ll gcd(ll a,ll b)
{
return b?gcd(b,a%b):a;
}
void exgcd(ll a,ll b,ll &x,ll &y)
{
if(!b) x=1,y=0;
else exgcd(b,a%b,y,x),y-=a/b*x;
}
ll calc(ll p,ll q,ll a,ll b,ll v)
{
ll g=gcd(p,q),x,y;
if(v%g) return inf;
p/=g,q/=g,exgcd(p,q,x,y);
x*=v/g,x+=(a-x)/q*q;
while(x<a) x+=q;
while(x-q>=a) x-=q;
if(x>b) return inf;
}
int main()
{
read(T);
while(T--)
{
read(a),read(b),read(p),read(q),l=ans=0,r=q,p*=2,q*=2;
while(l<=r)
{
ll mid=(l+r)>>1;
if(check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
printf("%lld\n",min(calc(p,q,a,b,q/2-ans),calc(p,q,a,b,q/2+ans)));
}
return 0;
}