[线段树][ST表][堆]luogu P5967 [POI2016]Korale

题面

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\n",ans);
    DFS(1,ans);
}
ST表版
//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\n",ans);
    DFS(1,ans);
}
线段树版

 

posted @ 2021-03-25 19:20  Vagari  阅读(60)  评论(0编辑  收藏  举报