[HNOI2001] 求正整数 - 背包dp,数论

对于任意输入的正整数n,请编程求出具有n个不同因子的最小正整数m。

Solution

(乍一看很简单却搞了好久?我真是太菜了)

根据因子个数计算公式

\(m = \prod p_i^{q_i}\), 则 \(n = \prod (q_i + 1)\)

\(f[i][j]\) 为只包含前 \(j\) 个质因数,因子个数为 \(i\) 的最小的数

转移类似背包: \(f[i][j]=min_{k|i} (f[i/k][j-1] \cdot p_j^{k-1})\)

这样直接做是 \(O(n \sqrt n \log n)\) ,考虑到需要枚举的 \(i\) 有且仅有 \(n\) 的因数,而约数个数的一个宽上界是 \(O(\sqrt n)\),复杂度就压缩到了 \(O(n \log n)\)

使用高精度直接 dp 可能会复杂度爆炸,所以我们对数一下

#include 
using namespace std;

struct Biguint {
    int a[100005], len;

    Biguint() {
        memset(a, 0, sizeof a);
        len = 0;
    }

    void read() {
        string str;
        cin >> str;
        memset(a, 0, sizeof a);
        len = str.length();
        for (int i = 0; i < str.size(); i++)
            a[i] = str[str.length() - i - 1] - '0';
    }

    void print() {
        for (int i = len - 1; i >= 0; i--) {
            cout << a[i];
        }
    }

    bool operator < (const Biguint& obj) {
        const int* b = obj.a;
        if (this->len == obj.len) {
            for (int i = len - 1; i>=0; --i)
                if (a[i] != b[i]) return a[i] < b[i];
            return false;
        }
        else return this->len < obj.len;
    }

    bool operator > (const Biguint& obj) {
        const int* b = obj.a;
        if (this->len == obj.len) {
            for (int i = len - 1; i>=0; --i)
                if (a[i] != b[i]) return a[i] > b[i];
            return false;
        }
        else return this->len > obj.len;
    }

    bool operator != (const Biguint& obj) {
        return (*this < obj) | (*this > obj);
    }

    bool operator == (const Biguint& obj) {
        return !((*this < obj) | (*this > obj));
    }

    bool operator <= (const Biguint& obj) {
        return (*this) < obj || (*this) == obj;
    }

    bool operator >= (const Biguint& obj) {
        return (*this) > obj || (*this) == obj;
    }

    Biguint operator += (const Biguint& obj) {
        const int* b = obj.a;
        if (obj.len > len) len = obj.len;
        for (int i = 0; i < len; i++) {
            a[i] += b[i];
            if (a[i] >= 10) a[i + 1] += a[i] / 10, a[i] %= 10;
        }
        if (a[len]) ++len;
        while (a[len - 1] >= 10)
            a[len] += a[len - 1] / 10, a[len - 1] %= 10, ++len;
        return *this;
    }

    Biguint operator + (const Biguint& obj) {
        Biguint ret;
        ret += *this;
        ret += obj;
        return ret;
    }

    Biguint operator -= (const Biguint& obj) {
        const int* b = obj.a;
        for (int i = 0; i < len; i++) {
            a[i] -= b[i];
            if (a[i] < 0) a[i + 1]--, a[i] += 10;
        }
        while (a[len - 1] == 0 && len > 0) --len;
        return *this;
    }

    Biguint operator -(const Biguint& obj) {
        Biguint ret;
        ret += *this;
        ret -= obj;
        return ret;
    }

    Biguint operator *= (int b) {
        for (int i = 0; i < len; i++)
            a[i] *= b;
        for (int i = 0; i < len; i++)
            a[i + 1] += a[i] / 10, a[i] %= 10;
        ++len;
        while (a[len - 1] >= 10)
            a[len] += a[len - 1] / 10, a[len - 1] %= 10, ++len;
        while (a[len - 1] == 0 && len > 0) --len;
        return *this;
    }

    Biguint operator * (int b) {
        Biguint ret;
        ret = *this;
        ret *= b;
        return ret;
    }

    Biguint operator * (const Biguint& obj) {
        const int* b = obj.a;
        Biguint ret;
        for (int i = 0; i < len; i++)
            for (int j = 0; j < obj.len; j++)
                ret.a[i + j] += a[i] * b[j];
        for (int i = 0; i < len + obj.len; i++)
            ret.a[i + 1] += ret.a[i] / 10, ret.a[i] %= 10;
        ret.len = len + obj.len;
        ++ret.len;
        while (ret.a[ret.len - 1])
            ret.a[ret.len] += ret.a[ret.len - 1] / 10, ret.a[ret.len - 1] %= 10, ++ret.len;
        while (ret.a[ret.len - 1] == 0 && ret.len > 0) --ret.len;
        return ret;
    }

};

ostream& operator << (ostream& os, Biguint num)
{
    for (int i = num.len - 1; i >= 0; --i)
        os << num.a[i];
    if (num.len == 0) os << "0";
    return os;
}

istream& operator >> (istream& is, Biguint& num)
{
    string str;
    is >> str;
    memset(num.a, 0, sizeof num.a);
    num.len = str.length();
    for (int i = 0; i < str.length(); i++)
        num.a[i] = str[str.length() - i - 1] - '0';
    return is;
}


const int N = 500005;
const int p[21] = {0, 2,  3,  5,  7, 11, 13, 17, 19, 23, 29,
    31, 37, 41, 43, 47, 53, 59, 61, 67, 71};
int n,g[50005][21],h[50005][21],ii[50005],top;
double lp[21]={},f[50005][21];

signed main() {
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++) if(n%i==0) ii[++top]=i;
    for(int i=1;i<=20;i++) lp[i]=log(p[i]);
    for(int i=0;i<=50000;i++) for(int j=0;j<=20;j++) f[i][j]=1e9;
    f[1][0]=1;
    for(int _i=2;_i<=top;_i++) {
        int i=ii[_i];
        int sq=sqrt(i);
        for(int j=1;j<=20;j++) {
            for(int k=1;k<=sq;k++) {
                if(i%k==0) {
                    if(f[i][j]>f[i/k][j-1]+(k-1)*lp[j]) {
                        f[i][j]=f[i/k][j-1]+(k-1)*lp[j];
                        g[i][j]=i/k;
                        h[i][j]=k-1;
                    }
                }
            }
            for(int u=1;u<=sq;u++) {
                int k=i/u;
                if(i%k==0) {
                    if(f[i][j]>f[i/k][j-1]+(k-1)*lp[j]) {
                        f[i][j]=f[i/k][j-1]+(k-1)*lp[j];
                        g[i][j]=i/k;
                        h[i][j]=k-1;
                    }
                }
            }
        }
    }
    int pos=n;
    Biguint ans;
    ans.len=1;
    ans.a[0]=1;
    for(int j=20;j;--j) {
        int i=pos;
        for(int k=1;k<=h[i][j];k++) ans*=p[j];
        pos=g[i][j];
    }
    cout<

你可能感兴趣的:([HNOI2001] 求正整数 - 背包dp,数论)