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伪光滑数的主要内容,如果未能解决你的问题,请参考以下文章