Loading

【题解】[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;
}
posted @ 2021-06-11 21:02  7KByte  阅读(95)  评论(0编辑  收藏  举报