埃氏筛/线性筛+质数与约数一本通题解

埃氏筛:

筛选 \(1...n\) 中所有的质数

考虑一个质数 \(x\) ,它的\(2x,3x,4x...n/x*x\)都是合数,打上标记即可 \(O(NloglogN)\)

for(int i=2;i<=n;i++){
    if(vis[i])  continue;
    p[++cnt]=i;
    for(int j=i;j<=n/i;j++){
        vis[i*j]=1;
    }
}

线性筛:

考虑一个合数会在埃氏筛中筛去多次,所以线性筛只会让合数被最小的质数删去,考虑一个 \(i\) ,当筛到 \(i\) % \(p[j]\)\(i*p[j]\)\(p[j]\)\(i*p[j]\) 的最小质因子,然而考虑一个 \(p[k]>p[j]\) 因为 \(i\) 可以写成\(p[j]*x\) 所以 \(p[j]\)一定是\(p[k]*i\) 的最小质因子,显然是错误的

for(int i=2;i<=n;i++){
	if(!vis[i]){
		p[++cnt]=i;
	}
	for(int j=1;j<=cnt&&p[j]*i<=n;j++){
		vis[i*p[j]]=p[j];
		if(i%p[j]==0)  break;
	}
} 

ybt题解

T1:

线性筛模板

T2:

考虑R-L非常的小啊,然后一个数必然有一个小于其根号大小的质数是它的因数,所以我们先预处理出小于R的根号大小的所有质数,然后用这些质数去筛[L,R]之中的数,没有被筛掉的数就是质数

考虑这样做复杂度为什么正确,考虑一个数最多有log个不同的质因子(考虑最小的质因子为2,最多有log个2),所以一个数最多被筛log次,复杂度为 \(O((R-L)*logR))\),况且还跑不满

T3:

首先转化一下式子(这一步恰恰是最难想到的)

T4:

先转化一下,求1~N中约数最多的数中最小的一个

在N的范围内,一个数不同的质因子个数不会超过10

然后贪心的考虑,要是一段连续的质数并且每个质数的个数单调不增,才能保证是最优的,然后我们就找到质数表中前十个,然后dfs它的个数即可

#include<bits/stdc++.h>
#define int long long
using namespace std;
int a[20]={0,2,3,5,7,11,13,17,19,23,29};
int n,s1,s;
void dfs(int x,int y,int z,int k){//xpri,yshu,zci,kyinshugeshu
    if(x==11)  return;
    int c=1;
    for(int i=1;i<=z;i++){
        c*=a[x];
        if(y*c>n)  return;
        if(k*(i+1)>s1){
            s1=k*(i+1);
            s=y*c;
        }
        else if(k*(i+1)==s1&&y*c<s){
            s=y*c;
        }
        dfs(x+1,y*c,i,k*(i+1));
    }
}
signed main(){
    scanf("%lld",&n);
    dfs(1,1,31,1);
    printf("%lld",s);
    return 0;
}

T5:

我们先转化一下,求 \(\sum_{i=1}^nk-\left \lfloor \frac{k}{i} \right \rfloor*i\)

然后就成了 \(n*k-\sum_{i=1}^n\left \lfloor \frac{k}{i} \right \rfloor*i\)

我们手模一下发现 \(\left \lfloor \frac{k}{i} \right \rfloor\) 这个东西是对于一段i是连续的,然后我们就可以根据这个规律把 \(\left \lfloor \frac{k}{i} \right \rfloor\) 相同的分为一个块

然后有多少个块呢,题解中证明了最多有 \(2*\sqrt n\) 个块,不做赘述

然后我们一个块的左端点l固定了,然后算出 \(t=\left \lfloor \frac{k}{l} \right \rfloor\) 右端点 \(r=\left \lfloor \frac{k}{t} \right \rfloor\)

然后知道了块长和左右块的端点,等差数列求和即可

T6:

切了,然后看了看了一眼ybt验证了一下,但是不知道它在写什么屎

把式子转化为 \((x+1)(y+1)=(h+1)\) 然后对h+1进行 \(O(\sqrt n)\) 的判断因数个数的操作即可,注意判断当两个因数相等时的情况

T7:

草,数据错误,根本就不用输出第二问,好不容易写了第二问,然后发现不用,就炸了

本来有个哥德巴赫猜想,可以用,但是我没太搞懂,好像是偶数可以被拆成两个质数之和,所以我们采取了一些暴力措施,我们暴力枚举,判断这些数的加和是否可以拆成两个质数之和,若不行,则我们取出一个质数3,然后就可以变成偶数了,再拆成两个质数就可以做了

为什么拆成两个质数是可行的呢?

我们采取一些贪心策略,当填充第一个质数时从大到小枚举,然后剩下的就是第二个质数所包含的,证毕

T8:

辗转相减法的逆运算,首先最优情况下最后的数对一定是 \((n,x),x<=n\) 然后我们暴力枚举所有x的情况,然后使用辗转相减法相减判断能否剩下数对 \((1,0)\) 然后根据辗转次数来统计答案即可

我之前一直以为这样减下去可能会漏情况,但是我发现对于一个确定的数对我们进行辗转相减法的话最后剩下的结果是固定的,而只有结果是 \((1,0)\) 时才是合法的

然后直接辗转相减法复杂度太高,我们可以考虑最优性剪枝,或者把多次重复性减法合并成一次除法就是辗转相除法即可

#include<bits/stdc++.h>
using namespace std;
int n,ans=1e6+5;
void gcd(int x,int y,int t){
    if(!y){
        if(x==1)  ans=min(ans,t);
        return ;
    }
    gcd(y,x%y,t+x/y);
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        gcd(n,i,0);
    }
    printf("%d",ans-1);
}

T9:

经典转化想到了,就是对于一个数统计对多少个区间产生贡献

然后我们就要找到在这个数左边和右边离它最近的因数,我们设这个位置为 \(l[i],r[i]\) 考虑一个区间可以有 \((i-l[i])\) 种开头, \((r[i]-i)\) 种结尾,所以最终产生的贡献就是 \((i-l[i])*(r[i]-i)\)

如何找到 \(l[i],r[i]\)

对于 \(l[i]\) ,我们可以对于一个数枚举它的因数,判断其因数最后一次出现的位置的最大值

\(r[i]\) 就是反着跑一遍即可

T10:

最后10分钟直接切了

直接暴力枚举所有数的因数,然后找到出现两次以上的因数的最大值,然后就做完了

posted @ 2024-12-10 21:20  daydreamer_zcxnb  阅读(11)  评论(0编辑  收藏  举报