@bzoj - [email 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 - 伪光滑数。

现在给出 N,求所有整数中第 K 大的 N - 伪光滑数。

input

只有一行,为用空格隔开的整数 N 和 K 。2 ≤ N ≤ 10^18, 1 ≤ K ≤ 800000,保证至少有 K 个满足要求的数

output

只有一行,为一个整数,表示答案。

sample input

12345 20

sample output

9167

@[email protected]

首先,如果对于质数 x 以及一个整数 k,满足 \(x^k \le N\)。则只要保证剩下的 k-1 个质因数小于等于 x 就可以构成伪光滑数了。

所以假如我们确定了 x 和 k,题目中给出的关于伪光滑数的约束统统没有用。

@version - [email protected]

关于求解第 k 大,这里显然二分答案是不可做的。我们可以通过类比 k 短路的做法来做。简略地说一下:

当给定 x 和 k 时,显然最大的数是 k 个 x 相乘,我们把这个当作初始状态。考虑设计一个不重复不遗漏的初始状态与某一状态之间的转移,且要使得转移始终是递减的。

假如某状态 \(m = p_1^{a_1}*p_2^{a_2}*\dots*p_r^{a_r}\),其中 \(p_1<p_2<\dots<p_r=x\)。可以把这个状态看作 \([1, a_1+a_2+\dots+a_r]\) 这些位置填 \(p_r\),再 \([1, a_1+a_2+\dots+a_{r-1}]\) 这些位置填 \(p_{r-1}\) 并覆盖掉之前填的质因子,然后重复操作……

于是,我们这样来转移:状态里面存储当前的数是什么,最小质因子和次小质因子是什么,最小质因子和次小质因子的出现次数。每次两类转移,次小换最小或者新增更小。前者要保证次小的质因子次数为正。

@version - [email protected]

当然……这并不是我的重点。

对于这道题,我们有一个更暴力(应该是吧?毕竟内存消耗更多些)的方法。

我们一样给定 x 和 k。然后对于每一对 (x, k),我们把所有的可能的答案存储在堆内。

当然不可能是直接存,我们使用可持久化左偏树来搞。

定义 \(f(x, k)\) 表示最大的质因子为 x,质因子个数为 k 时的左偏树。

讨论次大的数是什么,可以得到 \(f(x, k)\) 是由所有满足 i < x 且 i 为质数的左偏树 \(f(i, k-1)\) 全部合并起来再给所有元素乘 x 得到。

后一个操作可以用打 tag 的方式实现。

但是这样空间开销还是很大。我们考虑给左偏树求前缀和。

定义 \(g(x, k)\) 表示所有满足 i <= x 且 i 为质数的左偏树 \(f(i, k)\) 全部合并起来得到的左偏树。

就有 \(g(x, k)\) 等于 \(f(x, k)\) 与 \(g(x-1, k)\)的合并,\(f(x, k)\) 等于 \(g(x, k-1)\) 这棵树整体乘 x。

这样每增加一个状态最多只会多合并一次。

对,f 和 g 都是一个左偏树。

对,你可以理解为左偏树用来作 dp。

对,很神奇,我也没见过。

下传标记的时候记得也要新建结点。

@accepted [email protected]

因为我实在是太懒了,仅给出第二种方法的代码。

#include<queue>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXK = 800000;
struct node{
    node *ch[2];
    ll key, tag; int dis;
}pl[16000000 + 5], *rt1[128][64], *rt2[128][64], *tcnt, *NIL;
void pushdown(node *x) {
    if( x->tag != 1 ) {
        if( x->ch[0] != NIL ) {
            tcnt++; (*tcnt) = *(x->ch[0]);
            tcnt->tag *= x->tag, tcnt->key *= x->tag;
            x->ch[0] = tcnt;
        }
        if( x->ch[1] != NIL ) {
            tcnt++; (*tcnt) = *(x->ch[1]);
            tcnt->tag *= x->tag, tcnt->key *= x->tag;
            x->ch[1] = tcnt;
        }
        x->tag = 1;
    }
}
struct node2{
    node *x;
    node2(node *_x):x(_x){}
};
bool operator < (node2 a, node2 b) {
    return a.x->key < b.x->key;
}
node *newnode(ll x) {
    tcnt++;
    tcnt->ch[0] = tcnt->ch[1] = NIL;
    tcnt->key = x, tcnt->tag = 1, tcnt->dis = 1;
    return tcnt;
}
node *merge(node *x, node *y) {
    if( y == NIL ) return x;
    if( x == NIL ) return y;
    node *p = (++tcnt);
    if( x->key < y->key ) swap(x, y);
    (*p) = (*x); pushdown(p);
    p->ch[1] = merge(p->ch[1], y);
    if( p->ch[0]->dis < p->ch[1]->dis ) swap(p->ch[0], p->ch[1]);
    p->dis = p->ch[1]->dis + 1;
    return p;
}
void init() {
    NIL = tcnt = &pl[0];
    NIL->dis = 0;
    for(int i=0;i<64;i++)
        rt2[0][i] = NIL;
    for(int i=0;i<128;i++)
        rt2[i][0] = NIL;
}
bool is_prm(int n) {
    for(int i=2;i<n;i++)
        if( n % i == 0 ) return false;
    return true;
}
int prm[128], mxk[128], pcnt;
priority_queue<node2>que;
int main() {
    ll N; int K; init();
    scanf("%lld%d", &N, &K);
    for(int i=2;i<128;i++) {
        if( !is_prm(i) ) continue;
        prm[++pcnt] = i;
        for(ll nw=1;nw<=N/i;nw*=i) mxk[pcnt]++;
    }
    for(int i=1;i<=pcnt;i++)
        if( mxk[i] ) {
            rt1[i][1] = newnode(prm[i]), rt2[i][1] = merge(rt2[i-1][1], rt1[i][1]);
            for(int j=2;j<=mxk[i];j++) {
                rt1[i][j] = (++tcnt); (*rt1[i][j]) = (*rt2[i][j-1]);
                rt1[i][j]->key *= prm[i], rt1[i][j]->tag *= prm[i];
                rt2[i][j] = merge(rt2[i-1][j], rt1[i][j]);
            }
    /*for(node *i=&pl[1];i<=tcnt;i++)
        printf("%d %d %d %lld %lld\n", i-pl, i->ch[0]-pl, i->ch[1]-pl, i->key, i->tag);*/
        }
    for(int i=1;i<=pcnt;i++)
        for(int j=1;j<=mxk[i];j++)
            que.push(rt1[i][j]);
    ll ans;
    for(int i=1;i<=K;i++) {
        node2 f = que.top(); que.pop();
        pushdown(f.x);
        que.push(node2(merge(f.x->ch[0], f.x->ch[1])));
        ans = f.x->key;
    }
    printf("%lld\n", ans);
}

@[email protected]

一开始我在合并的时候,某一个结点等于 NIL 的时候新建了一个结点来存另一个结点。

但是极限数据始终不是 RE 就是 MLE。

后来我选择直接返回另一个结点,然后就没问题了。

连这个空间也要卡吗……

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10287082.html

时间: 2024-11-06 14:18:10

@bzoj - [email protected] [Cqoi2016]伪光滑数的相关文章

【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

[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]的数的集合. 然后

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

@bzoj - [email&#160;protected] [POI2015] Kinoman

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 共有 m 部电影,第 i 部电影的好看值为 w[i]. 在 n 天之中每天会放映一部电影,第 i 天放映的是第 f[i] 部. 你可以选择 l, r (1 <= l <= r <= n) ,并观看第 l, l+1, -, r 天内所有的电影. 最大化观看且仅观看过一次的电影的好

@bzoj - [email&#160;protected] [Poi2011]Lightning Conductor

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @version - [email protected] @version - [email protected] @[email protected] @[email protected] 已知一个长度为

@bzoj - [email&#160;protected] [POI2014]Hotel加强版

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @[email protected] 给定一棵树,求无序三元组 (a, b, c) 的个数,使得 dis(a, b) = dis(b, c) = dis(c, a),且 a ≠ b, b ≠ c, c ≠ a. inpu

@bzoj - [email&#160;protected] 旅行规划

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 请你维护一个序列,支持两种操作: (1)某个区间 [x, y] 内的数同时加上一个增量 k. (2)询问某一个区间 [x, y] 中从 1 开始的最大前缀和. input 第一行给出一个整数 n.n <= 100000.接下来一行 n 个整数表示序列的初始值. 第三行给出一个整数 m,

@bzoj - [email&#160;protected] Hard Nim

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] n 堆石子,每堆石子的数量是不超过 m 的一个质数. 两个人玩 nim 游戏,问使后手必胜的初始局面有多少种. 模 10^9 + 7. input 多组数据.数据组数 <= 80. 每组数据一行两个正整数,n 和 m.1 <= n <= 10^9, 2 <= m <