码迷,mamicode.com
首页 > 其他好文 > 详细

【CQOI 2016】伪光滑数

时间:2020-04-06 15:26:33      阅读:78      评论:0      收藏:0      [点我收藏+]

标签:getchar   pac   查询   long   solution   priority   ++   bool   display   

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\n", ans);
    }
}T;

int main() {
    n = read(), k = read();
    T.init(); T.solve();
    return 0;
}

【CQOI 2016】伪光滑数

标签:getchar   pac   查询   long   solution   priority   ++   bool   display   

原文地址:https://www.cnblogs.com/AWhiteWall/p/12642021.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!