[题解]CF1063D

0. 概述

硬推式子呗。
首先应该注意到这个数据范围很奇怪:\(10^{11}\) 看起来像是要求根号做法,那么不妨根号分治,而根号分治需要以暴力做法为基础,所以我们先考虑一些暴力做法。

1. 贪心一

首先能想到的一个暴力是枚举贪者个数 \(ans\),我们从 \(0\) 枚举到 \(n\)。显然此时每圈都会拿走 \(n+ans\) 个,这样 \([l,\ r]\) 就会拿走 \(k\bmod (n+ans)\) 个,设这个数为 \(cnt\),那么可以根据题目要求推出一些限制:

\[\begin{aligned} cnt&\le \min\{ans,\ len\}+len\\ cnt&\ge \max\{0,\ ans-n+len-1\}+len \end{aligned} \]

上式的意义是尽量全放到区间里,下式的意义是尽量全不放区间里,注意最后剩下一个时贪者会少取一个。
显然满足这个式子的数都可以构造出合法方案,所以只需要找到最大的满足条件的 \(ans\) 即可。

2. 贪心二

贪心一要求 \(n\) 比较小,那么想一想有没有能除以 \(n\) 的贪心。
嘿,您猜怎么着?还真有:圈数是 \(O(k/n)\) 级别的,我们看看枚举圈数 \(tot\) 会发生什么。
仍然设贪者有 \(ans\) 个,\([l,\ r]\) 拿走了 \(cnt\) 个,一圈一共拿走 \(k_0\) 个,那么有以下限制:

\[\begin{aligned} k&=tot\times k_0+cnt\\ k&\le \min\{ans,\ len\}+len+tot\times k_0\\ k&\ge \max\{0,\ ans-n+len-1\}+len+tot\times k_0\\ k_0&=cnt+n \end{aligned} \]

二三式子的意义就是贪心一中 \(cnt\) 的限制加上整圈的贡献,我们尝试化简一下这个式子,让一边只有 \(ans\)
这时候可以先拆掉 \(\max\)\(\min\) 变成两个式子,然后再化简,即如下四个式子:

\[\begin{aligned} k&\le ans+len+tot\times (ans+n)\\ k&\le len+len+tot\times (ans+n)\\ k&\ge ans-n+len-1+len+tot\times (ans+n)\\ k&\ge len+tot\times (ans+n) \end{aligned} \]

化简之后是这样的,我们又得到了答案的一个限制:

\[\begin{aligned} ans&\ge\dfrac{k-len-tot\times n}{tot+1}\\ ans&\ge\dfrac{k-2len-tot\times n}{tot}\\ ans&\le\dfrac{k+n-2len+1-tot\times n}{tot+1}\\ ans&\le\dfrac{k-len-tot\times n}{tot} \end{aligned} \]

只要这个区间存在,我们就可以取最大值作为答案,需要注意两个细节:

  1. \(cnt=0\) 的情况可能存在,但不能使用这个式子,需要单独讨论,式子很简单可以见代码。
  2. 因为 \(ans\) 是整数,所以下界需要上取整,上界需要下取整。

3. 正解

有了上面两个贪心做铺垫,我们可以在 \(n\le\sqrt{10^{11}}\) 时用贪心一,其他情况用贪心二,这样复杂度是 \(O(\sqrt{n})\)

4. 代码

int n, k, l, r;
signed main(){
  Read(n), Read(l), Read(r), Read(k);
  int len=r-l+1;
  if(len<=0)len=n+len;
  if(n<=100000){
    per(i, n, 0){
      int tmp=k%(i+n);
      if(!tmp)tmp+=i+n;
      if(max(0ll, i-n+len-1)+len<=tmp&&tmp<=len+min(len, i)){
        return printf("%lld\n", i), 0;
      }
    }
    puts("-1");
  }else {
    int ans=-1;
    rep(i, 1, k/n){
      int mx=min((k+2*n-2*len+1)/(i+1), (k-len)/i);
      if(mx>=max((k-2*len+i-1)/i, (k+n-len+i)/(i+1)))ans=max(ans, mx-n);
    }
    if(len<=k&&k<=2*len){
      ans=max(ans, n-len+k-len+(k<2*len));
    }
    printf("%lld\n", ans);
  }
  return 0;
}
posted @ 2022-09-02 15:03  ajthreac  阅读(22)  评论(0编辑  收藏  举报