Codeforces1182F Maximum Sine (类欧几里得)

传送门
f ( x ) = abs ( sin ( p q π x ) ) f(x) = \text{abs}(\text{sin}(\frac{p}{q} \pi x)) f(x)=abs(sin(qpπx))
求整数x在[a,b]之间 f x f_x fx最大值
这道题官方给的题解是分块暴力?参考qzh巨佬题解,我也用类欧几里得做的这道题
首先sin非常不友善,我们发现这题可以转化为求 p x q \frac{px}{q} qpx最接近 k + 1 2 k+\frac {1}{2} k+21 x x x
p ⋅ x   m o d   q p\cdot x \ mod \ q px mod q 最接近 q 2 \frac{q}{2} 2q
同时乘2,得 2 p ⋅ x   m o d   2 q 2p\cdot x \ mod \ 2q 2px mod 2q 最接近 q q q
考虑转化为可行性问题,二分mid,则问题转化为是否存在x使得 2 p ⋅ x   m o d   2 q 2p\cdot x \ mod \ 2q 2px mod 2q [ q − m i d , q + m i d ] [q-mid,q+mid] [qmid,q+mid]上有取值
问题等价于求 ∑ x = a b ⌊ p x − l q ⌋ − ⌊ p x − r − 1 q ⌋ \sum_{x=a}^b\lfloor\frac{px-l}{q}\rfloor - \lfloor\frac{px-r-1}{q}\rfloor x=abqpxlqpxr1是否大于0其中 l = q − m i d , r = q + m i d l=q-mid,r=q+mid l=qmid,r=q+mid
这个意思就是说看你这段里跨没跨过一个q的整数倍,如果跨过了那么这两个值就会不同
用类欧几里得 f x f_x fx求解即可
最后用exgcd还原x
Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; 
int t;
int a,b,p,q;
ll gcd(int n,int a,int b,int c)
{
	//cout<<n<<" "<<a<<" "<<b<<" "<<c<<endl;
	if(n==-1)return 0;
	if(a==0)return 1ll*(b/c)*(n+1);
	if(a>=c||b>=c)
	{
		return 1ll*n*(n+1)/2*(a/c)+1ll*(n+1)*(b/c)+gcd(n,a%c,b%c,c);
	}
	ll m=(1ll*a*n+b)/c-1;
	//cout<<m<<" "<<c<<" "<<c-b-1<<" "<<a<<"fdsafsdfsa"<<endl;
	return 1ll*n*(m+1)-gcd(m,c,c-b-1,a);
}
ll sum(int x,int y,int a,int b,int c)
{
	return gcd(y,a,b,c)-gcd(x-1,a,b,c);
}
bool check(int l,int r)
{
	return sum(a,b,p,q-l,q)-sum(a,b,p,q-r-1,q)>0?1:0;
}
void exgcd(int a,int b,ll &x,ll &y)
{
	if(b==0)
	{
		x=1;y=0;
		return ;
	}
	exgcd(b,a%b,y,x);
	y-=a/b*x;
}
ll sol(int p,int q,int a,int b,int t)
{
	int gc=__gcd(p,q);
	if(t%gc)return 1e18;
	p /= gc, q /= gc, t /= gc;
    ll x, y;
    exgcd(p, q, x, y);
    //cout<<x<<" "<<y<<endl;
    x *= t, y *= t;
    x += 1ll * (a - x) / q * q;
    while(x < a) x += q;
    while(x - q >= a) x -= q;
    if(x > b) return 1e18;
    return x;  
}
void solve()
{
	scanf("%d%d%d%d",&a,&b,&p,&q);
	p*=2,q*=2;
	int l=0,r=q/2;
	while(l<r-1)
	{
		int mid=l+r>>1;//cout<<mid<<endl;
		if(check(q/2-mid,q/2+mid))r=mid;
		else l=mid;
		
	}//cout<<"gfdgdsf"<<endl;
	long long ans;
	if(check(q/2-l,q/2+l))ans=l;
	else ans=r;
	
	printf("%lld\n",min(sol(p,q,a,b,q/2-ans),sol(p,q,a,b,q/2+ans)));
}
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		solve();
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值