BZOJ4524 [CQOI2016]伪光滑数 可持久化可并堆+DP

很有意思的一个题

考场上我竟然乱搞出这种奇怪的以前想都没想过的算法(以前根本想不到函数式数据结构还可以拿来DP)

其实写这个题解我也是不想这个有趣的方法就这么绝迹了呢。。。

因为是求第K大所以不难想到用堆的K路归并问题,对答案的种类我们分类,最直观的分类就是按最大因子和分解项数来分类

即用f[i,j]表示最大质因子为p[i],用了j项分解数的数的集合,因为要求数的不重不漏,我们力求让所有的数都由互素的小数集合扩展得到,为了获得之前所有的数,我们保存g[i,j]为f[i,j]的前缀和,意为前i种素因子的所有数集,不难得到DP方程:

BZOJ4524 [CQOI2016]伪光滑数 可持久化可并堆+DP_第1张图片


其中加号是集合的并,只要这些集合可以归并、求最大值,就可以在外层用堆维护每个集合的最大值,每次取最大的一个,删除最大值,还可以对集合进行乘法,而这一些需求均满足可并堆的性质,因此我们用可持久化可并堆来作为数值,进行DP。

可并堆的乘法用标记实现,标记下传时新建节点即可。注意中间过程可能爆long long,乘法要判>0防炸。

#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
struct node{
	int i,k;LL v;
	node(int a,int b,LL c){i=a;k=b;v=c;}
	bool operator<(node b)const{return v<b.v;}
};
priority_queue<node>q;
struct Heap{LL v,tg;int l,r;}c[16000005];
LL N;
int K,tot=0,p[505],vst[505],f[505][505],g[505][505];
int NewNode(int x,LL tg){
   if(x==0)return 0;
   tot++;c[tot]=c[x];c[tot].v*=tg;c[tot].tg*=tg;
   return tot;
}
void Pushdown(int x){
	 if(c[x].tg!=1){
	 	c[x].l=NewNode(c[x].l,c[x].tg);
	    c[x].r=NewNode(c[x].r,c[x].tg);
	    c[x].tg=1;
	 }
}
int merge(int x,int y){
	if(!x||!y)return x+y;
	Pushdown(x);Pushdown(y);
	if(c[x].v<c[y].v)swap(x,y);
	int z=++tot;c[z]=c[x];
	c[z].l=merge(c[x].r,y);
	c[z].r=c[x].l;c[z].tg=1;
	return z;
}
void init(){
	 int i,j,k;LL pr,prm;
	 scanf("%lld%d",&N,&K);
	 for(i=2;i<=128;i++)
	    if(!vst[i]){
	    	p[++p[0]]=i;
	    	for(j=i+i;j<=128;j+=i)vst[j]=1;
	    }
	 f[0][0]=1;tot=1;g[0][0]=1;
	 c[f[0][0]].v=c[f[0][0]].tg=1;
	 for(i=1;i<=p[0];i++){
	 	f[i][0]=f[0][0];g[i][0]=1;
	 	for(pr=p[i],j=1;pr>0&&pr<=N;j++,pr=pr*p[i]){
	 		f[i][j]=0;
	 		for(prm=p[i],k=1;k<=j;k++,prm*=p[i])
	 		    f[i][j]=merge(f[i][j],NewNode(g[i-1][j-k],prm));
	 		q.push(node(i,j,c[f[i][j]].v));
	 		g[i][j]=merge(g[i-1][j],f[i][j]);
	    }
	 }
}
void solve(){
	 int i,j;
	 for(i=1;i<=K;i++){
	 	node x=q.top();q.pop();
	 	if(i==K){
	 		printf("%lld\n",x.v);
	 		return;
	 	}
	 	Pushdown(f[x.i][x.k]);
	 	f[x.i][x.k]=merge(c[f[x.i][x.k]].l,c[f[x.i][x.k]].r);
	 	q.push(node(x.i,x.k,c[f[x.i][x.k]].v));
	 }
}
int main(){
    init();
    solve();
	return 0;
}


你可能感兴趣的:(dp,可持久化可并堆)