[bzoj4524][CQOI2016]伪光滑数

题目描述

这里写图片描述

做法

先把质数都筛进数组p。
我们设f[i,j]表示分解后最大质数为p[i],分解的结果是j项的数的集合。
为了顺利的推出f[i,j]我们还要设g[i,j]表示f的前缀和。
先让我们重定义一些符号:
1、a+b返回集合a与集合b的并(a与b均为集合)
2、a*b表示集合a内所有数乘上b(a为集合,b为一个数)
那么转移是这样的:
f[i,j]=jk=1g[i1,jk]p[i]k
g[i,j]=g[i1,j]+f[i,j]
那么这个集合是什么呢?当然就是可持久化可并堆啦!

统计答案

有一种显然的做法,那就是设一个集合ans,表示所有符合条件的f的和。
然后就在ans中弹k-1个出来就好了。
然后这样做所需空间很大,会崩。
其实我们只需要把符合条件的f对应的堆顶扔进一个大堆中,大堆按照结点的值,大的游戏。
然后同样是在大堆里弹k-1个。
如果这样做空间还是挂了,请参考代码以及置顶的好东西里面关于可持久化的一些内容。

防止爆long long

枚举状态其实只要这样就好

fo(i,1,top){
        fo(j,1,floor(log(n)/log(p[i])))

参考程序

#include<cstdio>
#include<algorithm>
#include<set>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=18000000+10;
int dis[maxn],left[maxn],right[maxn],p[50];
ll key[maxn],mul[maxn];
int f[200][100],g[200][100];
bool bz[200];
int i,j,k,l,t,m,tot,top;
ll n;
struct dong{
    int id;
    dong(int x){id=x;}
};
bool operator <(dong a,dong b){
    return key[a.id]>key[b.id]||key[a.id]==key[b.id]&&a.id<b.id;
}
multiset<dong> ans;
void make_list(){
    fo(i,2,127)
        if (!bz[i])
            fo(j,2,127/i)
                bz[i*j]=1;
    fo(i,2,127){
        if (i>n) break;
        if (!bz[i]) p[++top]=i;
    }
}
ll qsm(int x,int y){
    if (!y) return 1;
    ll t=qsm(x,y/2);
    t=t*t;
    if (y%2) t=(ll)t*x;
    return t;
}
int newnode(int x){
    if (!x) return 0;
    int t=++tot;
    left[t]=left[x];
    right[t]=right[x];
    dis[t]=dis[x];
    key[t]=key[x];
    mul[t]=mul[x];
    return t;
}
int mark(int x,ll y){
    int t=newnode(x);
    key[t]*=y;
    mul[t]*=y;
    return t;
}
void down(int x){
    if (mul[x]>1){
        left[x]=mark(left[x],mul[x]);
        right[x]=mark(right[x],mul[x]);
        mul[x]=1;
    }
}
int merge(int a,int b){
    if (!a) return newnode(b);
    if (!b) return newnode(a);
    down(a);down(b);
    if (key[a]<key[b]) swap(a,b);
    int t=newnode(a);
    right[t]=merge(right[a],b);
    if (dis[right[t]]>dis[left[t]]) swap(left[t],right[t]);
    dis[t]=dis[right[t]]+1;
    return t;
}
int deletemin(int x){
    down(x);
    return merge(left[x],right[x]);
}
int main(){
    scanf("%lld%d",&n,&m);
    make_list();
    g[0][0]=f[0][0]=dis[1]=key[1]=mul[1]=tot=1;
    fo(i,1,top){
        fo(j,1,floor(log(n)/log(p[i]))){
            fo(k,1,j){
                t=mark(g[i-1][j-k],qsm(p[i],k));
                f[i][j]=merge(f[i][j],t);
            }
            ans.insert(dong(f[i][j]));
            g[i][j]=merge(g[i-1][j],f[i][j]);
        }
        g[i][0]=g[i-1][0];
    }
    m--;
    while (m--){
        t=(*ans.begin()).id;
        ans.erase(ans.begin());
        ans.insert(dong(merge(left[t],right[t])));
    }
    printf("%lld\n",key[(*ans.begin()).id]);
}

你可能感兴趣的:([bzoj4524][CQOI2016]伪光滑数)