题目
https://www.luogu.com.cn/problem/CF346E
思路
考试的时候花了3h没推出来(其实已经很接近了)
首先有一个比较显然的结论:如果一个数落在 ( p − a ∗ ( p / a ) , n ] (p-a*(p/a),n] (p−a∗(p/a),n] 上,那么这个数是没用的,容易证明前面的最大距离一定大于它。
然后我们发现,如果我们走完一个周期,每次的起始位置变化是有规律的。可以发现,走完一个周期,它的起始位置会加上 a − ( p m o d a ) a-(p\ mod\ a) a−(p mod a),也可以发现对于每一个长度为 a a a 段是一样的。所以这里就变成了一个子问题。
我们把一个问题用三元组表示: ( a , n , p ) (a,n,p) (a,n,p),那经历一次周期之后就会变成 ( m i n ( p m o d a , a − ( p m o d a ) ) , c n t , a ) (min(p\ mod \ a,a-(p\ mod\ a)),cnt,a) (min(p mod a,a−(p mod a)),cnt,a),其中 c n t cnt cnt 表示走过的有效周期数。
好的问题来了 c n t cnt cnt 怎么求呢?直接 a ∗ n / p a*n/p a∗n/p?恭喜你,你GG了。
我们发现如果一个周期没有跑完,那么总会有间隔特别大,那么这个周期就是无效的。
那怎么判断这个东西呢?还记得第一个显然的结论吗?只要最后一个落点大于一开始倒数第二个落点就是有效的啦!也就是 a ∗ ( n m o d p ) > = a ∗ ( p / a − 1 ) a*(n\ mod\ p)>=a*(p/a-1) a∗(n mod p)>=a∗(p/a−1)
如果 a ∗ n a*n a∗n 没有超出周期显然可以直接统计答案
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll work(ll a,ll n,ll p)
{
if(a*n<p) return max(a,p-a*n);
ll aii=a*n/p;
if(a*n%p<a*(p/a-1)) aii--;
return work(min(p%a,a-p%a),aii,a);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
ll a,n,p,h,yjy;
scanf("%lld%lld%lld%lld",&a,&n,&p,&h);
a%=p;
if(a*n<p) yjy=a;else yjy=work(a,n,p);
if(yjy>h) printf("NO\n");else printf("YES\n");
}
}