2025寒假营第三场 G数论分块 题解

智乃的博弈游戏

https://ac.nowcoder.com/acm/contest/95335/A

G

求n模i的余数的前k大的和,其中i属于

首先是一个和整除,求和有关的东西,先想想数论分块。实际上

n模i=n-floor(n/i)*i

然后在整除分块的一个块里是一样的,所以在一个块里,余数是一个等差数列。然后我们要求前k大的余数的和,可以找到第k大的的余数的值,然后对于大于这个值的余数,直接求和,对于等于这个值的余数,取一定个数的求和,直到求和元素总个数等于k

那么我们首先需要找到第k大余数的值,这可以二分第k大实现,我们每次check就遍历一遍所有块,把大于mid的值的个数求和,如果总数小于,就缩小mid,反之增大mid。计算一个块大于mid的余数个数时,我们可以先算出这个等差数列的首项和公差,然后可以计算小于等于mid的元素有多少个,总数减去小于等于的个数,就是大于的个数。(当然也可以直接算大于的个数,我是因为一开始写的就是小于就按这个写了)

然后找到第k大值之后,再遍历一遍所有块,把每个块里大于该值的元素求和,这里也采用和前面类似的思路,大于该值的元素的和=元素总和-小于等于该值的元素的和,并计算选择了多少个元素。

最后看选择的元素个数比k小多少,剩余到k的元素值肯定都是第k大的元素的值,用第k大元素补齐

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n,k;
    cin>>n>>k;
    int l=0,r=n;
    auto check=[&](int m)->bool{
        int l=1,r;
        int sum=0;
        while(l<=n){
            r=n/(n/l);
            int x=n%r;
            int y=n/r;
            sum+=r-l+1-min((m-x+y)/y,r-l+1);
            l=r+1;
        }
        return sum<k;
    };
    while(l<=r){
        int m=l+r>>1;
        if(check(m))r=m-1;
        else l=m+1;
    }
    int val=l;
    auto cal=[&](long long x,int y,long long n)->long long{
        return (x+x+(n-1)*y)*n/2;
    };
    l=1;
    long long ans=0;
    int cnt=0;
    while(l<=n){
        r=n/(n/l);
        int x=n%r;
        int y=n/r;
        int t=(val-x+y)/y;
        cnt+=r-l+1-min((val-x+y)/y,r-l+1);
        //cout<<l<<' '<<r<<' '<<val<<' '<<t<<'\n';
        ans+=cal(x,y,r-l+1)-cal(x,y,min(r-l+1,t));
        l=r+1;
    }
    cout<<ans+1ll*val*(k-cnt);
}
全部评论

相关推荐

不愿透露姓名的神秘牛友
昨天 17:17
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
07-10 15:58
投个小米提前批试试水,先投一个岗位看看形势,不行就再沉淀一下投第二个岗位,莫辜负
Java抽象带篮子:我嘞个骚刚,已经开始研发6g了吗
投递小米集团等公司7个岗位
点赞 评论 收藏
分享
05-22 09:23
门头沟学院 Java
点赞 评论 收藏
分享
半解316:内容充实,细节需要修改一下。 1,整体压缩为一页。所有内容顶格。 2,项目描述删除,直接写个人工作量 修改完之后还需要建议,可以私聊
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务