码迷,mamicode.com
首页 > 其他好文 > 详细

数论分块

时间:2020-02-20 22:25:11      阅读:102      评论:0      收藏:0      [点我收藏+]

标签:src   event   scan   namespace   sig   min   turn   printf   space   

数论分块大致用于处理形如求Σ(1,n)  (k div i) 的问题

打表易得,(k div i)的值是线性的,因为向下取整,所以会出现值成段的现象,这样我们原先暴力的O(n)的算法可以得到优化

首先我们要知道一个定理

对于(k div i)而言最多有2√k个取值

 

证明:对于 i (1 <= i <= n, 且 i 是整数)而言,i 可以分成两种情况

  1. i <= √k , i 最多有 √k 个取值
  2. i >= √k , 那么 (k div i)<= √k,最多有√k个取值

所以我们可以拥有一个O(√k)的复杂度


 

对于怎么确定一个块值得左右边界,这里直接给出结论

对于一个块而言,假设左边界为LT,右边界为RT。那么有等式 RT = K / (K / LT)

 


 

所以我们就可以形如下式进行操作

 

 for(ll l = 1, r = 0 ; l <= n ; l = r + 1)
    {
        if(k / l) r = min(n, k/(k/l)) ;
        else r = n ;
        do something
    }

 

为了显得这篇文章不那么划水,所以还是要带一题例题

BZOJ 1257

题意就是让我们求 Σ(1,n) (k mod i)

那么怎么转换到今天的知识呢?

我们都知道 k mod i = k - (k div i) * i 

原式 = n* k - Σ(1,n) (k div i) * i 

对于一个值块(l , r)而言 (k div i) 的值是确定的,我们可以把它看成常数,这里假设成T,那么对于值块(l , r) 后一块可转换成 (k div i) * Σ(l,r) i 等差数列求和

这题就搞定了

技术图片
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n, k, ans = 0 ;
int main(int argc, char const *argv[])
{
    scanf("%lld %lld",&n,&k) ;
    ans = k * n ;
    for(ll l = 1, r = 0 ; l <= n ; l = r + 1)
    {
        if(k / l) r = min(n, k/(k/l)) ;
        else r = n ;
        ans -= (k / l) * (r - l + 1) * (l + r) / 2ll ;
    }
    printf("%lld\n",ans) ;
    return 0;
}
View Code

 

数论分块

标签:src   event   scan   namespace   sig   min   turn   printf   space   

原文地址:https://www.cnblogs.com/wifePI/p/12337615.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!