bzoj 4524: [Cqoi2016]伪光滑数

看到求第k大,k很小,首先应该想到k路归并。
利用可持久化可并堆进行dp
g[i][j]表示前i个素数,用了j个质因子可以表示的数的集合
g[i][j]=sigma(g[i-1][j-k]*p[i]^k) sigma表示集合的并,乘法用打标记实现。
然后k路归并,按照使用了的质因子个数分类。
令mx[j]表示取j个质因子的情况下可以取得最大素数
用堆维护g[mx[j]][j]的最大值,每次出堆后在对应的可并堆里删除这个值,并把新的最大值加入全局堆。
另,可持久化可并堆的写法:
(1)在merge的时候要新建节点
(2)在release的时候,i不用新建节点,但是i的左右儿子都要新建节点。
实测数组大小开1.7*10^7
    
    
    
    
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
#define inf 1e9
#define eps 1e-8
#define md
using namespace std;
struct QQ { int x,y; ll val;};
struct cmp
{
bool operator () (QQ a,QQ b) { return a.val<b.val;}
};
priority_queue<QQ,vector<QQ>,cmp> q;
struct Tr { int l,r; ll val,mul;} tr[17000010];
int dis[17000010],f[32][100],g[32][100],mx[100];
int ss[32]={1,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127};
int cnt;
int New(int x,ll d)
{
if (!x) return 0;
cnt++; tr[cnt]=tr[x]; tr[cnt].val*=d; tr[cnt].mul*=d;
//if (cnt%1000000==0) printf("%d\n",cnt);
return cnt;
}
 
void release(int i)
{
if (tr[i].mul!=1)
{
ll d=tr[i].mul; tr[i].mul=1;
tr[i].l=New(tr[i].l,d); tr[i].r=New(tr[i].r,d);
}
}
 
int merge(int x,int y)
{
if (!x) return y;
if (!y) return x;
release(x); release(y);
if (tr[x].val<tr[y].val) swap(x,y);
int z=++cnt;
//if (cnt%1000000==0) printf("%d\n",cnt);
tr[z].val=tr[x].val; tr[z].mul=1;
tr[z].l=tr[x].l; tr[z].r=merge(tr[x].r,y);
if (dis[tr[z].l]<dis[tr[z].r]) swap(tr[z].l,tr[z].r);
dis[z]=dis[tr[z].r]+1;
return z;
}
 
int main()
{
//printf("%d\n",(sizeof(tr)+sizeof(dis))/1024/1024);
ll n; int K;
scanf("%lld%d",&n,&K);
cnt=1; g[0][0]=1; tr[1]=(Tr){0,0,1,1}; q.push((QQ){0,0,1});
for (int i=1;i<=31;i++)
{
f[i][0]=g[i][0]=1;
ll pr=ss[i];
for (int j=1;pr<=n&&pr%ss[i]==0;j++,pr*=ss[i])
{
g[i][j]=g[i-1][j];
ll pri=ss[i];
for (int k=1;k<=j;k++,pri*=ss[i]) g[i][j]=merge(g[i][j],New(g[i-1][j-k],pri));
//for (int k=1;k<=cnt;k++) printf("%d : %d %d\n",k,tr[k].l,tr[k].r);
//printf("pr j %lld %d %d %d: %lld\n",pr,j,f[i][j],g[i][j],tr[f[i][j]].val);
mx[j]=max(mx[j],i);
}
}
for (int j=1;mx[j];j++) q.push((QQ){mx[j],j,tr[g[mx[j]][j]].val});
ll ans=0;
for (int i=1;i<=K;i++)
{
int x=q.top().x,y=q.top().y; ans=q.top().val; q.pop(); //printf("%lld\n",ans);
release(g[x][y]); g[x][y]=merge(tr[g[x][y]].l,tr[g[x][y]].r);
q.push((QQ){x,y,tr[g[x][y]].val});
}
printf("%lld\n",ans);// printf("%d\n",cnt);
return 0;
}

你可能感兴趣的:(bzoj 4524: [Cqoi2016]伪光滑数)