1. 程式人生 > >約數 求反素數bzoj1053 bzoj1257

約數 求反素數bzoj1053 bzoj1257

//約數

/*
求n的正約數集合:試除法
複雜度:O(sqrt(n))
原理:掃描[1,sqrt(N)],嘗試d能否整除n,若能,則N/d也能 
*/
int factor[1600],m=0; 
for(int i=1;i*i<=n;i++){
    if(n%i==0){
        factor[++m]=i;
        if(i!=n/i) factor[++m]=n/i;
    }
} 

/*
求[1,n]每個數的正約數集合:倍數法
複雜度:O(nlogn)
原理:對於每個數d,[1,n]中以d為約數的數就是d的倍數 
*/
vector<int> factor[500010];
for(int i=1;i<=n;i++) for(int j=1;i*j<=n;j++) factor[i*j].push_back(i); /*兩個推論 1.一個整數的約數個數上界 為2*sqrt(n) 2.[1,n]每個數的約數個數總和大約為nlogn */
/*
題意有點繞
每個人手裡有一個非0數字,首先第k個人出列,然後按其手裡的數字讓下一個人出列
迴圈如此,設i為小於等於N的最大反素數,問第i個出列的人的編號
打表求反素數:按質因數大小遞增順序搜尋每一個質因子,列舉每一個質因子的個數
唯一分解定理,一個數的因子數:(p1+1)(p2+1)(p3+1)...
性質1:一個反素數的質因子必然是從2開始連續的質數
性質2:
1 2 3 4 6 9 12 18 36
*/ #include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define ll long long #define inf 1<<29 int n,p[]={0,2,3,5,7,11,13,17,19,23,29,31},use[20]; ll maxt,ans;//maxt是最大因子數,ans是當前的反質數 //id是素數表的下標,now是當前數字,tot是因子數,符合因子數公式 void dfs(ll id,ll now,ll tot){ if(tot>maxt)//
找到了因子數更大的數 ans=now,maxt=tot; else if(tot==maxt && now<ans)//找到因子數相同,但是數值更小的數 ans=now,maxt=tot; use[id]=0; while(now*p[id]<=n && use[id]+1<=use[id-1]){//第二個是剪枝 use[id]++; now*=p[id]; dfs(id+1,now,tot*(use[id]+1)); } } int main(){ scanf("%d",&n); use[0]=inf; dfs(1,1,1); printf("%lld",ans); return 0; }

推公式題,詳見進階指南p134

思路大概是:當i在區間[x,k/(k/x)]時,k/i的值都是一樣的,那麼這一段的值可以用等差數列求和公式做

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
long long n,k;
long long ans;
int main(){
    scanf("%lld%lld",&n,&k);
    ans=(long long)n*k;
    for(int x=1,gx=0;x<=n;x=gx+1){
        gx=k/x?min(n,k/(k/x)):n;
        ans-=(k/x)*(x+gx)*(gx-x+1)/2;//首項x*k/x,末項gx*k/x,項數gx-x+1 
    }
    printf("%lld\n",ans);
}