首页 > 其他分享 >数论分块简介

数论分块简介

时间:2023-03-31 13:12:57浏览次数:30  
标签:分块 数论 简介 ll long 如下 solve 区间

简单介绍一下数论分块的思想。空说无益,先上几道题。

题1:P1403 约数研究

链接如下:https://www.luogu.com.cn/problem/P1403

 

 

如果这道题要对每一个数进行分解、统计,未免太麻烦。我们不妨换个思路,假设这里的N是30,那么这个区间内整体的数字分布如下图:

 

 

 这里我们先选择求解有多少数会以2为因子,这很好求,任何形如2k的数都满足这个条件,这里标黄,如下图所示:

 

 

 因为在这个区间内k的变化是连续的(自然数意义上),所以我们要求得有多少k在这里满足需求,只需要将N除以因子2即可,在本例中k是等于15。

再考虑当因子为3时的情况,如下图,标蓝的数字即为满足要求的数字:

 

 

 这里的k也很好求,等于10

再考虑当因子等于25时会有几个数满足要求,很显然只有25自己满足。

这些因子出现次数的求解不是无止境的,因为因子的数量不可能大于N本身,所以我们便将上述问题转换为如下形式:

 

 

 即便将问题转换为如此方便的形式,我们依然可以更进一步,这里以N=15为例,我们观察一下上述求和的运作过程:

 

 

 我们观察到上述存在着区间内值相等的情况,我们可以考虑使用分块的思想。

假设左边界为i,现在我们需要求右边界可以延伸到何处。先确定此处的函数值为N/i,我们可以知晓后续的k满足

 

这里满足要求的k的取值即为

 

 

 这里的k即为满足要求的右边界,在左右边界内的函数值都是相同的,那求和也就很简单,直接区间函数值乘以区间长度即可。

可以证明这个方法的时间复杂度为,具体不做证明

 

 

上题的代码如下:

 1 #include <iostream>
 2 #define ll long long
 3 using namespace std;
 4 ll n;
 5 void solve(ll n1){
 6     ll ans=0;
 7     ll r=0;
 8     for (int i=1;i<=n1;i=r+1){
 9         r=n1/(n1/i);
10         ans+=(n1/i)*(r-i+1);
11     }
12     cout<<ans;
13 }
14 int main(){
15     cin>>n;
16     solve(n);
17     return 0;
18 }

题2:NC13221 数码

题目链接如下:https://ac.nowcoder.com/acm/problem/13221

题目截图如下:

 

 这里所用到的也是数论分块的思想,只不过改为了统计最高位数码出现的次数。这里需要采用前缀和的知识来记录每一个数量级对出现次数的贡献度,实现相对复杂。

代码如下:

 1 #include <iostream>
 2 #define ll long long
 3 using namespace std;
 4 ll b[15],rem[20],l,r;
 5 void get_num(ll num,ll opt){
 6     ll m=num;
 7     ll g=0,t=0;
 8     while(m){
 9         t++;
10         g=m%10;
11         m/=10;
12     }
13     for (int i=1;i<=9;i++)
14         rem[i]+=opt*b[t-1];
15     for (int i=1;i<g;i++)
16         rem[i]+=opt*(b[t]-b[t-1]);
17     rem[g]+=opt*(num-g*(b[t]-b[t-1])+1);
18 }
19 void solve(ll start,ll opt){
20     ll end=0;
21     for (int i=1;i<=start;i=end+1){
22         end=start/(start/i);
23         get_num(end, opt*start/i);
24         get_num(i-1,-1*opt*start/i);
25     }
26 }
27 int main(){
28     ll ans=1;
29     for (int i=1;i<=10;i++){
30         b[i]=b[i-1]+ans;
31         ans*=10;
32     }
33     cin>>l>>r;
34     solve(r,1);
35     solve(l-1,-1);
36     for (int i=1;i<10;i++)
37         cout<<rem[i]<<endl;
38     return 0;
39 }

题3:P2424 约数和

题目链接如下:https://www.luogu.com.cn/problem/P2261

题目截图如下:

 

 这一题主要改求解出现次数为出现数字和,那么整体的求和形式变为

 

 

 这里分区间考虑,在一个区间内是相同的,将形式转换为,在区间内做等差数列求和即可。

 

代码如下:

 1 #include <iostream>
 2 #define ll long long
 3 using namespace std;
 4 ll a1,a2;
 5 ll solve(ll n1){
 6     ll res=0;
 7     ll r=0;
 8     for (int l=1;l<=n1;l=r+1){
 9         r=n1/(n1/l);
10         res+=(n1/l)*(r-l+1)*(r+l)/2;
11     }
12     return res;
13 }
14 int main(){
15     cin>>a1>>a2;
16     cout<<solve(a2)-solve(a1-1);
17     return 0;
18 }

 

标签:分块,数论,简介,ll,long,如下,solve,区间
From: https://www.cnblogs.com/johnsonstar/p/17275943.html

相关文章

  • 孤儿进程和僵尸进程简介
    孤儿进程父进程运行结束后,但子进程还在运行(为运行结束),这样的子进程就称为孤儿进程(OrphanProcess)。每当出现一个孤儿进程的时候,内核几把故而进程的父进程设置为init(进程号为1),而init进程会循环地wait()已经退出的子进程。这样,当一个孤儿进程结束了其生命周期的时候,init进程......
  • Magento Helper简介
    正如许多其他的PHPMVC系统一样,Magento也有帮助类(HelperClasses)。这些类用来提供一些不适合放在模型,视图或者控制器中的功能。Magento的帮助类也是采用分组类名的机制。也......
  • Hypervisor简介
    初识Hypervisor https://zhuanlan.zhihu.com/p/185946700Hypervisor介绍 https://blog.csdn.net/u011086209/article/details/116756009       ......
  • Intent的简介以及属性的详解
    一.Intent的介绍Intent的中文意思是“意图,意向”,在Android中提供了Intent机制来协助应用间的交互与通讯,Intent负责对应用中一次操作的动作、动作涉及数据、附加数据进行描述......
  • Unity之PBR两种工作流简介
    关于PBR工作流,看下unity内置shader的设定:  可以看到有2种工作流,Specular和Metallic。它们的区别如下:  也就是前者用了一张SpecularMap,后者用的是MetallicMap。......
  • Linux 简介
    Linux简介Linux内核最初只是由芬兰人林纳斯·托瓦兹(LinusTorvalds)在赫尔辛基大学上学时出于个人爱好而编写的。Linux是一套免费使用和自由传播的类Unix操作系统,是......
  • SHA-256 简介及 C# 和 js 实现【加密知多少系列】
    〇、简介SHA-256是SHA-2下细分出的一种算法。截止目前(2023-03)未出现“碰撞”案例,被视为是绝对安全的加密算法之一。SHA-2(安全散列算法2:SecureHashAlgorithm2)是一......
  • nodejs学习笔记(一)——Node简介
    MarkDown的使用#标题1##标题2```代码片段```>内容引用+列表1+xxx-xxx+列表21.xxx2.xxx[官网链接](https://www.baidu.com)N......
  • 数论--原根(循环群生成元)
    对于素数p,如果存在一个正整数1<a<p,使得a1,a2,…,ap−1模p的值取遍1,2,…,p−1的所有整数,称a是p的一个原根(primitiveroot),其实就是循环群的生成元。如果aj≡......
  • 第三章 列表简介
    列表是什么#用[]来表示列表,列表中的元素用,隔开list=['element_one','element_two']#访问列表元素程序员的数学中开头的数字不是1,而是0print(list[0])#打印li......