• 【CQOI 2016】伪光滑数


    Solution

    又是一道神仙题。蒟蒻表示不看题解根本不会做

    首先我们定义一个 DP 数组 (mathtt{f[i][j]}) 表示:最大质因子为 (mathtt{p[i]}),分解成 j 个质数(可以相同)组成的集合(其中 (mathtt{f[i][j]}) 是这个集合的根节点,在这里我们用左偏树)。我们知道,只要得到了这个 DP,我们就可以把这个玩意儿的权值塞进队列排序,再插入儿子进行查询。

    为了求得这个数组,我们需要再定义一个 (mathtt{g[i][j]}) 表示:最大质因子的编号小于等于 i,分解成 j 个质数(可以相同)的集合。

    那么就有:

    [mathtt{g[i][j]=g[i-1][j]+f[i][j]} ]

    然后 f 数组就可以这样得到:

    [mathtt{f[i][j]=sum_{k=1}^{j}g[i-1][j-k]*p[i]^k} ]

    由于 DP 的特殊性,我们不能直接在左偏树上修改,所以要建可持久化左偏树。

    其中加法是左偏树的合并,乘法是在权值上乘上一个数。

    最后解释一下:我们的权值其实就是所求的 M。(mathtt{f[i][j]}) 的儿子其实就是乘一个 (mathtt{p[i]^k}) 就能满足成为最大质因子为 (mathtt{p[i]}),有 j 个质数的 M,只不过没有 (mathtt{val[f[i][j]]}) 大而已。我们把每个 (mathtt{p[i]^k}) 设为 (mathtt{lazy}) 标记,将懒标记一个个乘下去再用左偏树维护仍然保证成为最大质因子为 (mathtt{p[i]}),有 j 个质数的 M。

    Code

    #include <queue>
    #include <cstdio>
    #include <iostream>
    using namespace std;
    typedef long long ll;
    
    const int N = 17000005;
    
    ll n;
    int cnt, p[130], siz, k, f[130][130], g[130][130];
    bool vis[130];
    priority_queue < pair < ll, pair <int, int> > > q;
    
    ll read() {
        ll x = 0, f = 1; char s;
        while((s = getchar()) < '0' || s > '9') if(s == '-') f = -1;
        while(s >= '0' && s <= '9') {x = (x << 1) + (x << 3) + (s ^ 48); s = getchar();}
        return x * f;
    }
    
    void Prime() {
        for(int i = 2; i < 128; ++ i) {
            if(! vis[i]) p[++ cnt] = i;
            for(int j = 1; p[j] * i < 128; ++ j) {
                vis[p[j] * i] = 1;
                if(i % p[j] == 0) break;
            }
        }
    }
    
    struct LT {
        int dis[N], son[N][2];
        ll val[N], la[N];
    
        int newnode(const int x, const ll k) {
            if(! x) return 0;
            dis[++ siz] = dis[x]; la[siz] = la[x] * k;
            val[siz] = val[x] * k;
            son[siz][0] = son[x][0]; son[siz][1] = son[x][1];
            return siz;
        }
    
        void pushDown(const int o) {
            if(! o || la[o] == 1) return;
            son[o][0] = newnode(son[o][0], la[o]);
            son[o][1] = newnode(son[o][1], la[o]);
            la[o] = 1;
        }
    
        int unite(int o, int y) {
            if(! o || ! y) return o | y;
            if(val[o] < val[y]) swap(o, y);
            pushDown(o);
            int x = newnode(o, 1);
            son[x][1] = unite(son[x][1], y);
            if(dis[son[x][0]] < dis[son[x][1]]) swap(son[x][0], son[x][1]);
            dis[x] = dis[son[x][1]] + 1;
            return x;
        }
    
        void init() {
            Prime();
            g[0][0] = ++ siz; la[siz] = val[siz] = 1;
            for(int i = 1; i <= cnt; ++ i) {
                g[i][0] = 1;//相当于没有点,指向根节点
                for(ll j = 1, lim = p[i]; lim <= n; lim *= p[i], ++ j) {
                    for(ll k = 1, pri = p[i]; k <= j; ++ k, pri *= p[i]) f[i][j] = unite(f[i][j], newnode(g[i - 1][j - k], pri));
                    g[i][j] = unite(g[i - 1][j], f[i][j]);
                    q.push(make_pair(val[f[i][j]], make_pair(i, j)));
                }
            }
        }
    
        void solve() {
            ll ans;
            while(k --) {
                ans = q.top().first;
                int i = q.top().second.first, j = q.top().second.second; q.pop();
                pushDown(f[i][j]); f[i][j] = unite(son[f[i][j]][0], son[f[i][j]][1]);
                q.push(make_pair(val[f[i][j]], make_pair(i, j)));
            }
            printf("%lld
    ", ans);
        }
    }T;
    
    int main() {
        n = read(), k = read();
        T.init(); T.solve();
        return 0;
    }
    
  • 相关阅读:
    ubuntu安装软件失败
    阶乘函数 注意事项
    汇编退出
    vim比较文件
    汇编调试
    Redis
    记录一次ajax使用
    oracle11g导出dmp文件时不能导出空表,导致缺表
    将小数点后的0去掉
    redis
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/12642021.html
Copyright © 2020-2023  润新知