CQOI 2016伪光滑数

Posted awhitewall

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
}

以上是关于CQOI 2016伪光滑数的主要内容,如果未能解决你的问题,请参考以下文章

@bzoj - 4524@ [Cqoi2016]伪光滑数

CQOI 2016伪光滑数

[Solution] [CQOI2016]手机号码

bzoj 4521: [Cqoi2016]手机号码

[CQOI2016]手机号码

「CQOI2016」不同的最小割