题面
https://www.luogu.com.cn/problem/P5967
分析
看这种可以线性扩展且要求第 k 小的问题,就容易想到超级钢琴的做法
初始将一个 (a[1],1) 加入小根堆 (sum,i) ,每次取出堆顶时可以扩展为 (sum+a[i+1],i+1) , (sum-a[i]+a[i+1],i+1) (i<n) 显然取出到第 k 个时即为第 k 小(k 预先减 1 除去 0 的情况)
那么考虑大小相同的情况,我们用一个 cnt 来记录
因为要求字典序最小,所以搜索的时候贪心地选择靠前的且小于当前值 sum 的,可以用线段树或者 ST表 来维护这个最小值
ST表占用内存过高,会影响运行速度,最后用的线段树
代码
#include <iostream> #include <cstdio> #include <queue> #include <stack> #include <cmath> #include <algorithm> using namespace std; typedef long long ll; const int Inf=1e9+1; const int N=1e6+10; struct Sum { ll val;int id; friend bool operator < (Sum a,Sum b) {return a.val>b.val;} }; int n,k,m,same,pw[20],lg2[N],a[N],f[N][20]; ll ans; priority_queue<Sum>q; int s[N],top; bool fin; inline int read(){ register int s=0,w=1; char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-')w=-1;ch=getchar();} while (ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*w; } inline void write(int x) { if(x>9) write(x/10);putchar(x%10+48);return; } inline int Min(int l,int r) {return min(f[l][lg2[r-l+1]],f[r-pw[lg2[r-l+1]]+1][lg2[r-l+1]]);} inline void DFS(int dep,ll sum) { if (!sum) { same--; if (!same) { for (register int i=1;i<=top;i++) write(s[i]),putchar(32); fin=1; } return; } if (dep>n) return; for (register int i=dep;i<=n;i++) { register int l=i,r=n; while (l<=r) { register int mid=l+r>>1; if (Min(l,mid)<=sum) i=mid,r=mid-1; else l=mid+1; } if (!i) return; s[++top]=i;DFS(i+1,sum-f[i][0]); top--;if (fin) return; } } int main() { n=read(),k=read();m=log2(n); pw[0]=1;for (register int i=1;i<=m;i++) pw[i]=pw[i-1]<<1; for (register int i=1,j=0;i<=n;i++) a[i]=read(),f[i][0]=a[i],lg2[i]=j,j+=(i==pw[j]); for (register int i=1;i<=m;i++) for (register int j=1;j+pw[i]-1<=n;j++) f[j][i]=min(f[j][i-1],f[j+pw[i-1]][i-1]); sort(a+1,a+n+1);q.push((Sum){a[1],1}); same=1;ans=0; for (register int i=1;i<k;i++) { register Sum now=q.top();q.pop(); if (now.val>ans) ans=now.val,same=0; if (now.val==ans) same++; if (now.id<n) q.push((Sum){now.val-a[now.id]+a[now.id+1],now.id+1}),q.push((Sum){now.val+a[now.id+1],now.id+1}); } printf("%lld ",ans); DFS(1,ans); }
//ST占内存太高过不了 #include <iostream> #include <cstdio> #include <queue> #include <algorithm> #define lson (x<<1) #define rson ((x<<1)+1) using namespace std; typedef long long ll; const int Inf=1e9+1; const int N=1e6+10; struct Sum { ll val;int id; friend bool operator < (Sum a,Sum b) {return a.val>b.val;} }; int n,k,m,same,a[N],t[4*N],rev[N]; ll ans; priority_queue<Sum>q; int s[N],top; bool fin; inline int read(){ register int s=0,w=1; char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-')w=-1;ch=getchar();} while (ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*w; } inline void write(int x) { if(x>9) write(x/10);putchar(x%10+48);return; } inline void Build(int x,int l,int r) { if (l==r) {t[rev[l]=x]=a[l];return;} int mid=l+r>>1; Build(lson,l,mid);Build(rson,mid+1,r); t[x]=min(t[lson],t[rson]); } inline int Query(int x,int l,int r,int k,ll lim) { if (k<=l) { if (t[x]>lim) return 0; if (l==r) return l; } int mid=l+r>>1,lef=0; if (k<=mid) lef=Query(lson,l,mid,k,lim); if (lef) return lef; return Query(rson,mid+1,r,k,lim); } inline void DFS(int dep,ll sum) { if (!sum) { same--; if (!same) { for (register int i=1;i<=top;i++) write(s[i]),putchar(32); fin=1; } return; } if (dep>n) return; for (register int i=dep;i<=n;i++) { if (!(i=Query(1,1,n,i,sum))) return; s[++top]=i;DFS(i+1,sum-t[rev[i]]); top--;if (fin) return; } } int main() { n=read(),k=read(); for (register int i=1,j=0;i<=n;i++) a[i]=read(); Build(1,1,n);sort(a+1,a+n+1);q.push((Sum){a[1],1}); same=1;ans=0; for (register int i=1;i<k;i++) { register Sum now=q.top();q.pop(); if (now.val>ans) ans=now.val,same=0; if (now.val==ans) same++; if (now.id<n) q.push((Sum){now.val-a[now.id]+a[now.id+1],now.id+1}),q.push((Sum){now.val+a[now.id+1],now.id+1}); } printf("%lld ",ans); DFS(1,ans); }