【题解】[POI2016]Korale
给定 \(n\) 个数,可以选则其中的若干个数,代价为选择的数之和。求第 \(k\) 小代价和方案。代价相同比较方案的字典序。
首先对将数从小到大排序,然后用堆维护二元组 \((i,j)\) 表示代价为 \(i\) ,上一个选择的是第 \(j\) 个数。
那么有两种决策,第一种是选择第 \(j+1\) 个数,转移到状态 \((i+a_{j+1},j+1)\)。第二种是反悔操作,转移到状态 \((i+a_{j+1}-a_j,j+1)\) 。
然后考虑输出方案数。由于 \(k\le 10^6\),考虑直接按字典序暴搜出所有前 \(k\) 的方案。
那么我们需要支持查询区间中 \(\le lim\) 的最左边的位置。
很经典的问题,直接线段树上二分即可。由于区间有一端为右端点,所以直接树状数组上倍增也可以,常数更小。
一个细节是第 \(k\) 小的价值为 \(Ans\) ,那么我们在 \(k\) 中先减去 \(<ans\) 的方案数,然后再在 \(=ans\) 的方案中选择第 \(k\) 个。
为什么要这样做?因为如果这些数比较集中,那么 \(=ans\) 的方案数是指数级别的,找到所有 \(\le ans\) 的方案再求第 \(k\) 大是不现实的。
本题有一个加强版,有兴趣的读者可以思考一下[CCO2020] Shopping Plans。
时间复杂度 \(\mathcal{O}(k\log k+k\log n)\) 。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 1000005
#define int long long
using namespace std;
typedef pair<int,int> Pr;
int n,k,u[N],c[N],ans,sta[N],top;
priority_queue<Pr>q;
#define F first
#define S second
struct node{
int l,r,mn;
}a[N<<2];
#define L a[x].l
#define R a[x].r
#define ls (x<<1)
#define rs (ls|1)
#define M a[x].mn
void build(int x,int l,int r){
L=l;R=r;
if(l==r)M=u[l];
else{
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
M=min(a[ls].mn,a[rs].mn);
}
}
int ask(int x,int l,int r,int val){
if(a[x].mn>val)return ~0;
if(L>=l&&R<=r){
if(L==R)return L;
if(a[ls].mn<=val)return ask(ls,l,r,val);
if(a[rs].mn<=val)return ask(rs,l,r,val);
return ~0;
}
int mid=(L+R)>>1;
if(mid>=l){
int cur=ask(ls,l,r,val);
if(~cur)return cur;
}
if(mid<r){
int cur=ask(rs,l,r,val);
if(~cur)return cur;
}
return ~0;
}
void calc(int x,int res){
//cout<<"ww "<<res<<" "<<k<<endl;
//rep(i,1,top)printf("%d ",sta[i]);putchar('\n');
if(!res){
k--;
if(!k)return ;
}
if(x>n)return;
while(x<=n){
int cur=ask(1,x,n,res);
if(-1==cur)return;
sta[++top]=cur;
calc(cur+1,res-u[cur]);
if(!k)return;
top--;
x=cur+1;
}
}
void dfs(int x,int res){
//cout<<"cc "<<res<<endl;
// rep(i,1,top)printf("%d ",sta[i]);putchar('\n');
k--;if(!k)return;
if(x>n)return;
while(x<=n){
int cur=ask(1,x,n,res);
if(-1==cur)return;
sta[++top]=cur;
dfs(cur+1,res-u[cur]);
if(!k)return;
top--;
x=cur+1;
}
}
signed main(){
scanf("%lld%lld",&n,&k);
rep(i,1,n)scanf("%lld",&u[i]),c[i]=u[i];
sort(c+1,c+n+1);
q.push(make_pair(-c[1],1));
rep(i,1,k-2){
Pr cur=q.top();q.pop();
if(cur.S<n){
q.push(make_pair(cur.F-c[cur.S+1],cur.S+1));
q.push(make_pair(cur.F+c[cur.S]-c[cur.S+1],cur.S+1));
}
}
if(k>1)printf("%lld\n",ans=-q.top().F);else printf("%lld\n",ans=0);
build(1,1,n);dfs(1,ans-1);
//cout<<"ss "<<k<<endl;
calc(1,ans);
rep(i,1,top)printf("%lld ",sta[i]);putchar('\n');
return 0;
}