模拟搜索+线段树
我觉得这题挺妙的啊。
注意到当n=1000000很大的时候会有2^1000000种取法,但题目只要求选到k=1000000个,也就是我们不能爆搜,但要保证每一次都能取到一个前k大的。
也就是要进行一个优秀的搜索。考虑朴素的垃圾搜索,是每次枚举i选或者不选,然后搜索i+1。注意到搜索的下一层的和总大于上一层。因此把其中有用的节点拿出来,是一个树形结构,若当前在第i层,有两种决策:取i、放弃前一个取的数然后取i。显然这样只需维护当前搜索的最小的边界,用一个堆即可。
输出方案,考虑爆搜。注意到搜索的过程中每一次合法的临时答案都会在前k个答案里面出现,也就是如果能进行一个优秀的搜索我们就能只做O(k)次,同样考虑垃圾搜索,是每次暴力枚举下一个能放进来的,那么用线段树维护区间最小即可优化暴力枚举,从而实现优秀搜索。
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define N 1000005
#define mkp(_i,_j) make_pair(_i,_j)
using namespace std;
int in()
{
char c = getchar(); int r = 0;
for(; c < '0' || c > '9'; c = getchar());
for(; c >='0' && c <='9'; r=r*10+c-'0', c = getchar());
return r;
}
typedef long long ll;
typedef pair<ll,int> pii;
int n, k, a[N], b[N], cnt, t[N<<4]; ll f[N];
void build(int x, int l, int r)
{
if(l == r) {t[x] = a[l]; return;} int mid = (l+r)>>1;
build(x<<1,l,mid); build(x<<1|1,mid+1,r);
t[x] = min(t[x<<1], t[x<<1|1]);
}
int query(int x, int l, int r, int ql, int qr, ll lim)
{
if(t[x] > lim) return -1; int mid = (l+r)>>1;
if(ql <= l && r <= qr)
{
if(l == r) return l;
return t[x<<1] <= lim ? query(x<<1,l,mid,ql,qr,lim) : query(x<<1|1,mid+1,r,ql,qr,lim);
}
int ret = -1;
if(ql <= mid) ret = query(x<<1,l,mid,ql,qr,lim);
if(ret == -1 && mid < qr) ret = query(x<<1|1,mid+1,r,ql,qr,lim);
return ret;
}
int sta[N], stacnt;
void dfs(int x, ll des)
{
if(!des)
{
if(--cnt == 0)
{
for(int i = 1; i <= stacnt; i++) printf("%d%c",sta[i]," \n"[i==stacnt]);
exit(0);
}
return;
}
for(; x <= n; x++)
{
x = query(1,1,n,x,n,des);
if(x == -1) return;
sta[++stacnt] = x;
dfs(x+1, des-a[x]);
--stacnt;
}
}
int main()
{
// freopen("data.in","r",stdin);
n = in(), k = in();
for(int i = 1; i <= n; i++) a[i] = b[i] = in();
sort(b+1, b+1+n);
priority_queue<pii,vector<pii>, greater<pii> > q;
q.push(mkp(0,0));
for(int i = 1; i <= k; i++)
{
pii p = q.top(); q.pop(); f[i] = p.first;
if(p.second < n)
{
q.push(mkp(p.first+b[p.second+1], p.second+1));
if(p.second) q.push(mkp(p.first-b[p.second]+b[p.second+1], p.second+1));
}
}
build(1,1,n);
printf("%lld\n",f[k]); int cur = 1; for(; f[cur] != f[k]; cur++); cnt = k-cur + 1;
dfs(1,f[k]);
}