埃氏篩/線性篩+質數與約數一本通題解

daydreamer_zcxnb發表於2024-12-10

埃氏篩:

篩選 \(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分鐘直接切了

直接暴力列舉所有數的因數,然後找到出現兩次以上的因數的最大值,然後就做完了

相關文章