1. 程式人生 > >Usaco Training Section 3.1 Humble Numbers

Usaco Training Section 3.1 Humble Numbers

給你一個素數集合S。若一個數的素因子均屬於S,則該數為醜數。求第n個醜數。

這題出得很妙,正解不好想。

法一:set

先把S中每個數放入set。每次取出set中的最小數,即為第i個醜數,並從s中刪除。再將它乘S中的所有數,放入set。

這個複雜度還行,O(nklog(n)),會t一個點。

#include<bits/stdc++.h>
#define ll long long
using namespace std;

inline int read(){
    int x=0;char c=getchar();
    while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x;
}

set<ll> s;
int a[101];
int main()
{
    freopen("humble.in","r",stdin);
    freopen("humble.out","w",stdout);
    int k=read(),n=read();
    for(int i=1;i<=k;++i) a[i]=read(),s.insert(a[i]);
    for(int i=1;i<n;++i){
        int x=*s.begin();s.erase(s.begin());
        for(int j=1;j<=k;++j) s.insert((ll)x*a[j]);
    }
    printf("%d\n",*s.begin());
}

這個方法其實還比較好想(反正我是自己想出來的),後一個方法就是神犇才能想出來的看題解的。

法二(正解)

有了第一個tle的方法,我們可以嘗試優化。首先外迴圈O(n)少不了。我們就只能優化中間的構造醜數的部分。

考慮到醜數的一種單調性,即對於任意a[j](S中的第j個數),若a[j]*ans[x]<=ans[i],則a[j]*ans[x]<=ans[m](m>i)。

即若a[j]*ans[x]<=ans[i],則用a[j]更新時,ans[1]~ans[x]就再也不會有用了。

於是,求ans[i]時,對於a[j],我們每次只需找最小的x,使得a[j]*ans[x]>ans[i-1]。a[i]=min{a[j]*ans[x]}

這樣複雜度就OK了,O(nk)。(維護每個x的總次數為n,所以2個巢狀for迴圈次數為nk+n,維護總次數為nk,程式迴圈次數為2nk+n)

#include<bits/stdc++.h>
#define ll long long
using namespace std;

inline ll read(){
    int x=0;char c=getchar();
    while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x;
}

ll a[101],ans[100001],s[100001];
int main()
{
    freopen("humble.in","r",stdin);
    freopen("humble.out","w",stdout);
    ll k=read(),n=read();
    for(ll i=1;i<=k;++i) a[i]=read();
    ans[0]=1;
    for(ll i=1;i<=n;i++){
        ll x=2147483647;
        for(ll j=1;j<=k;j++){
            while(a[j]*ans[s[j]]<=ans[i-1]) s[j]++;
            x=min(x,a[j]*ans[s[j]]);
        }
        ans[i]=x;
    }
    printf("%d\n",ans[n]);
}