【BZOJ4524】[Cqoi2016]偽光滑數 堆(模擬搜索)
阿新 • • 發佈:2017-11-19
整數 多少 while i++ size pop truct 滿足 答案
【BZOJ4524】[Cqoi2016]偽光滑數
Description
若一個大於1的整數M的質因數分解有k項,其最大的質因子為Ak,並且滿足Ak^K<=N,Ak<128,我們就稱整數M為N-偽光滑數。現在給出N,求所有整數中,第K大的N-偽光滑數。Input
只有一行,為用空格隔開的整數N和K 2 ≤ N ≤ 10^18, 1 ≤ K ≤ 800000,保證至少有 K 個滿足要求的數Output
只有一行,為一個整數,表示答案。Sample Input
12345 20Sample Output
9167題解:先打表打出前31個質數,然後枚舉每個質數作為最大質因子,算出此時最多能有多少項,那麽此時的最大值顯然=這個質數^項數。我們將其扔到堆中。
然後我們要做的就是每次從堆中取出最大的數,將他的一個質因子變小,然後再扔到隊列中去,重復k次。並且我們要保證我們的取法不會出現遺漏和重復。這就是一個套路了。我們試圖模擬搜索的過程。在搜索時,假如我們已經取了一些質數,他們的積為val,那麽我們可以再取一個val的最小質因子,或者繼續考慮下一個更小的質因子。現在我們要模擬這個方法:
我們維護四元組(val,mn,first,second)代表當前的數,最小的質因子編號,最小的質因子次數,次小的質因子次數。那麽如果我們從堆中取出一個數,我們可以用一個最小質因子來換一個次小質因子,或者用最小質因子的下一個質數來替換最小質因子。容易發現這樣是可以做到不重不漏的。由於每次我們只往堆中扔進去2個數,所以時間復雜度就是$O(Klog_K)$的。(看見那些時間復雜度是$O(31*Klog_K)$的我就想笑~)
#include <cstdio> #include <cstring> #include <iostream> #include <queue> #include <algorithm> using namespace std; typedef long long ll; int m; ll n; int p[]={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};//31 struct node { ll val; int mn,lst,sec; bool operator < (const node &a) const { return val<a.val; } }; priority_queue<node> q; int main() { scanf("%lld%d",&n,&m); register node x,y; register int i,j; for(i=1;i<=31;i++) { ll tmp; for(tmp=1,j=0;tmp<=n/p[i];j++,tmp*=p[i]); x.val=tmp,x.mn=i,x.lst=j-1,x.sec=0; q.push(x); } while(--m) { x=q.top(),q.pop(); if(x.mn) { y.val=x.val/p[x.mn]*p[x.mn-1],y.mn=x.mn-1,y.lst=1,y.sec=x.lst-1; q.push(y); } if(x.sec) { y.val=x.val/p[x.mn+1]*p[x.mn],y.mn=x.mn,y.lst=x.lst+1,y.sec=x.sec-1; q.push(y); } } x=q.top(); printf("%lld",x.val); return 0; }
【BZOJ4524】[Cqoi2016]偽光滑數 堆(模擬搜索)