CF346E-Doodle Jump【类欧】

正题

题目链接:https://www.luogu.com.cn/problem/CF346E


题目大意

给出\(a,n,p,h\),在每个\(ax\%p(x\in[0,n])\)的位置有一个关键点,询问是否所有相邻关键点之间的距离都不超过\(h\)


解题思路

没怎么写过类欧,这个题还是很坑的,需要考虑求一下\(h\)需要的最小值(相邻关键点直接距离的最大值)

首先第一个循环肯定都是\(ax\)的位置有关键点了,然后第二个循环开始是\(\lceil\frac{p}{a}\rceil a-p+ax\),然后每个循环的起点加一个\(\lceil\frac{p}{a}\rceil a-p\)。好像就可以用类欧把一个大问题缩减成一个小问题了。

考虑一下细节,首先是末尾那一段,也就是\(a\lfloor\frac{p}{a}\rfloor+1\sim p\)这一段是没有用的,因为如果这一段无法到达最末尾处,那么一定存在某个\(k\)使得\(ka\)无法到达\((k+1)a\)

然后考虑有多少个可行的循环,简单的看是\(\lfloor\frac{an}{p}\rfloor\),但是这样可能会有某些周期没有跑完的情况,那么后面那些间隔是没有变小的,考虑到我们求的是最大间隔,肯定是取后面的,所以此时要减一。

然后当\(an\leq p\)的时候就可以取答案了。

时间复杂度\(O(\log n)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
ll a,n,p,h,T;
ll solve(ll a,ll n,ll p){
    if(a*n<p)return max(a,p-a*n);
    ll z=a*n/p;
    if(a*n%p<p/a*a-a)z--;
    return solve((p+a-1)/a*a-p,z,a);
}
signed main()
{
    scanf("%lld",&T);
    while(T--){
        scanf("%lld%lld%lld%lld",&a,&n,&p,&h);
        a%=p;
        if(a<=h){puts("YES");continue;}
        if(a*n<=p){puts(h>=a?"YES":"NO");continue;}
        puts((solve(a,n,p)<=h)?"YES":"NO");
    }
    return 0;
}
posted @ 2021-01-22 20:07  QuantAsk  阅读(78)  评论(0编辑  收藏  举报