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);
}
全部评论

相关推荐

认真搞学习:28小登的建议,投算法岗不要写什么物理竞赛,互联网+,多写点项目,用什么算法做了什么。还有本科算法是不可能的开发你这个也没有项目啊
点赞 评论 收藏
分享
06-23 11:43
门头沟学院 Java
allin校招的烤冷...:我靠,今天中午我也是这个hr隔一个星期发消息给我。问的问题还是一模一样的😅
点赞 评论 收藏
分享
哈哈哈哈哈哈哈哈哈哈这个世界太美好了
凉风落木楚山秋:毕业出路老师不管,你盖个章他好交差就完事了,等你盖完毕业了就不关他事情了
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

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