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

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

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

时间: 2024-11-07 12:17:49

【CQOI 2016】伪光滑数的相关文章

bzoj4524【CQOI2016】伪光滑数

4524: [Cqoi2016]伪光滑数 Time Limit: 10 Sec  Memory Limit: 512 MB Submit: 257  Solved: 117 [Submit][Status][Discuss] Description 若一个大于R的整数J的质因数分解有F项,其最大的质因子为ak,并且满足ak^k≤N, ak<128,我们就称整数J为N-伪光滑数. 现在给出L,求所有整数中,第E大的N-伪光滑数. Input 只有一行,为用空格隔开的整数L和E. 2 ≤ N ≤ 1

【BZOJ-4524】伪光滑数 堆 + 贪心 (暴力) [可持久化可并堆 + DP]

4524: [Cqoi2016]伪光滑数 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 183  Solved: 82[Submit][Status][Discuss] Description 若一个大于R的整数J的质因数分解有F项,其最大的质因子为ak,并且满足ak^k≤N, ak<128,我们就称整数J为N-伪光滑数. 现在给出L,求所有整数中,第E大的N-伪光滑数. Input 只有一行,为用空格隔开的整数L和E. 2 ≤ N ≤ 10^1

【BZOJ4524】[Cqoi2016]伪光滑数 堆(模拟搜索)

[BZOJ4524][Cqoi2016]伪光滑数 Description 若一个大于1的整数M的质因数分解有k项,其最大的质因子为Ak,并且满足Ak^K<=N,Ak<128,我们就称整数M为N-伪光滑数.现在给出N,求所有整数中,第K大的N-伪光滑数. Input 只有一行,为用空格隔开的整数N和K 2 ≤ N ≤ 10^18, 1 ≤ K ≤ 800000,保证至少有 K 个满足要求的数 Output 只有一行,为一个整数,表示答案. Sample Input 12345 20 Sample

@bzoj - [email&#160;protected] [Cqoi2016]伪光滑数

目录 @description@ @[email protected] @version - [email protected] @version - [email protected] @accepted [email protected] @[email protected] @description@ 若一个大于 1 的整数 M 的质因数分解有 k 项,其最大的质因子为 \(A_k\),并且满足 \(A_k^k \le N\),\(A_k < 128\),我们就称整数 M 为 N - 伪光

[CQOI2016]伪光滑数

题目描述 若一个大于1的整数M的质因数分解有k项,其最大的质因子为Ak,并且满足Ak^K<=N,Ak<128,我们就称整数M为N-伪 光滑数.现在给出N,求所有整数中,第K大的N-伪光滑数. 题解 题面的k意思是将这个数质因数分解后所有的质因子的指数和. 我们先把128以内的所有素数找出来,然后做一个dp. 我们令dp[i][j]表示当前数的最大的质因子为p[i],当前所有素因子的指数和为j的数的集合. 我们再令g[i][j]表示当指数和位j时,所有最大质因子小于等于p[i]的数的集合. 然后

BZOJ 4521 CQOI 2016 手机号码 数位DP

4521: [Cqoi2016]手机号码 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 539  Solved: 325[Submit][Status][Discuss] Description 人们选择手机号码时都希望号码好记.吉利.比如号码中含有几位相邻的相同数字.不含谐音不 吉利的数字等.手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号 码单独出售.为了便于前期规划,运营商希望开发一个工具来自动统计号段中满足特征的号码

CQOI 2016 k远点对

题目大意:n个点,求第k远的点对的距离 KD树裸题 注意要用堆维护第k远 #include<bits/stdc++.h> #define ll unsigned long long #define maxn 100010 using namespace std; inline int read(){ int s=0;char ch=getchar(); for(;ch<'0'||ch>'9';ch=getchar()); for(;ch>='0'&&ch<

【CQOI2016纯净整合】BZOJ-4519~4524 (5/6)

感觉CQOI的难度挺好的,比较贴近自身,所以拿出来做了一下 CQOI2016 Day1 T1:不同的最小割 涉及算法:最小割/分治/最小割树 思路: 最小割树裸题,直接分治最小割,记录下答案,最后排序一下,统计不同的答案即可 CODE: #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace

CQOI2016游记

前情提要:我是丝薄,noip405的丝薄,所以这次省选特别虚 day0 上午随便切了两个题.背了下版. 下午看考场,环境还好.键盘也不错.评測姬非常好,就是人和人之间有点近,我回去买了耳塞(尽管并没实用上 晚上回去一边看板一边纠结,想自己万一进不了队怎么办,熬到十一点过最终睡了.我心态果然还是不够好啊,应该什么都不想的. day1 上午六点半就起来了,七点到学校.开车过去七点四十就到了重邮. 然后老师们讲了下比赛的注意事项,竟然要多存盘以防别人关错电脑233 然后就開始考试了. 一開始考试我就惊