BZOJ4524 [CQOI2016]伪光滑数 可持久化可并堆+DP
2016-04-12 12:52
330 查看
很有意思的一个题
考场上我竟然乱搞出这种奇怪的以前想都没想过的算法(以前根本想不到函数式数据结构还可以拿来DP)
其实写这个题解我也是不想这个有趣的方法就这么绝迹了呢。。。
因为是求第K大所以不难想到用堆的K路归并问题,对答案的种类我们分类,最直观的分类就是按最大因子和分解项数来分类
即用f[i,j]表示最大质因子为p[i],用了j项分解数的数的集合,因为要求数的不重不漏,我们力求让所有的数都由互素的小数集合扩展得到,为了获得之前所有的数,我们保存g[i,j]为f[i,j]的前缀和,意为前i种素因子的所有数集,不难得到DP方程:
其中加号是集合的并,只要这些集合可以归并、求最大值,就可以在外层用堆维护每个集合的最大值,每次取最大的一个,删除最大值,还可以对集合进行乘法,而这一些需求均满足可并堆的性质,因此我们用可持久化可并堆来作为数值,进行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; }
相关文章推荐
- mac/linux中vim永久显示行号、开启语法高亮
- C语言输入一个整数,输出其二进制位中1的个数
- Qt之溅射屏幕
- MySQL安装之“测试”
- 汇编语言计算斐波那契
- Android_实现星星控件_学习
- Monkey and Banana(基础DP)
- 简单理解Struts2中拦截器与过滤器的区别及执行顺序
- 在Unity3D中使用Visual Studio调试shader
- 用matlab训练数字分类的深度神经网络Training a Deep Neural Network for Digit Classification
- 算法训练 6-3判定字符位置
- Android之去掉ListView的点击背景
- centos6.5 iptables结合ipset批量屏蔽ip
- 火狐中正常显示页面的CSS样式,在IE下完全不识别,页面全乱了
- Android 自定义ViewGroup
- 卷积神经网络Convolutional Neural Networks
- 算法训练 9-7链表数据求和操作
- Angularjs的ng-repeat中去除重复的数据
- 直接插入排序
- 剑指offer面试题 求数组中连续子数组的最大和