#190. 第k小

其实我觉得这题应该叫第k大。
一道堆优化dp。
显然应该要先把每题的分数排序。然后我就得到了一个错误的贪心,马上爆零了QAQ。
dp的每个状态都记录三个值:当前题目i、该题得分j、总分S。本来应该在每个状态中记录所有题目的j,但是这显然不能AC,而且堆貌似不兹磁对含数组或vector的结构体进行操作否则我TM也不会爆零了QAQ)
为了帮(fang)助(zhi)理(bao)解(ling),先考虑一个部分分算法,即a[i]≤2的情况。
a[i]为1的情况很难进行dp,但很容易特判:直接加入总分,然后假装它没出现过233。
对于a[i]为2的情况,用b[0]表示较大数,b[1]表示较小数。除了起始状态i=1、j=0以外的其他状态都强制令j=1,然后就可以大力dp了。共两种状态转移:一种是该行不变(取1),下行取1,另一种是该行退回0,下行取1。发现如果将其理解成二进制数,用这样的方法就能全部枚举出来。
a[i]大于2的情况可以同理看作是a[i]进制数的一位,共3种状态转移:前两种与2相同,还有一种是i不变,j++。但是注意改行退回0的操作会造成重复,所以不妨只在无法执行j++的时候归零。到头来还是只有两种转移。
然后这题就被A掉了吗?难道按b[0]、b[1]的差值从小到大排就能保证后面的都对吗?没 错,确实可以。
一步步考虑。为了保证一开始的顺序正确,我们不得不进行sort。但是后面的状态被枚举到的顺序并不取决于其最前面的状态被枚的顺序。举个栗子,如果第一题的估分是100、99、1,第二题的估分是100、98、2,第一题确实会被排在前面,但100、2的估分必然比1、100的估分先背枚举。
这题的代码写得有点恶心(讲道理根本不是我在写,而是Scarlet给我听写),需要加以说明。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstdlib>
#include<cmath>
using namespace std;
#define rep(i,j,k) for(i=j;i<=k;++i)
#define per(i,j,k) for(i=j;i>=k;--i)
#define ll long long
#define db double
#define ldb long double
#define pli pair<ll,int>
#define mkp make_pair
#define X first
#define Y second
const int N=100005;
int n,k,a[N],c[N<<2],e[N],f[N];ll b[N<<2],d[N];
priority_queue<pli>pq;
bool cmp(int x,int y){return d[x]<d[y];}
int main(){
	int i,j,x;ll p,y=0,z;int q;
	scanf("%d%d",&n,&k);
	rep(i,1,n){
		scanf("%d",&a[e[i]=i]);
		if(a[i]==1){
			scanf("%lld",&z);
			y+=z;--i;--n;
			continue;
		}
		a[i]+=a[i-1];
		rep(j,a[i-1]+1,a[i]){
			scanf("%lld",&b[j]);
			c[j]=i;
		}
		sort(b+a[i-1]+1,b+a[i]+1);
		d[i]=b[a[i]]-b[a[i]-1];
		y+=b[a[i]];
	}
	sort(e+1,e+n+1,cmp);
	rep(i,1,n)f[e[i]]=i;
	printf("%lld\n",y);
	if(!(--k))return 0;
	pq.push(mkp(y-b[a[e[1]]]+b[a[e[1]]-1],a[e[1]]-1));
	do{
		p=pq.top().X;q=pq.top().Y;pq.pop();
		printf("%lld\n",p);
		if(!(--k))break;
		if(c[q]==c[q-1])pq.push(mkp(p-b[q]+b[q-1],q-1));
		if((j=f[c[q]])<n){
			x=a[e[j+1]]-1;y=p-b[x+1]+b[x];
			if(q==a[e[j]]-1)pq.push(mkp(y-b[q]+b[a[e[j]]],x));
			pq.push(mkp(y,x));
		}
	}while(1);
	return 0;
}

首先,通过膜黈黈黈的代码,我们知道可以一个数组b,用a[i]存每一道题目在b中的下标。这样不仅能避开vector,起到加速的作用,而且还能将堆中状态的i、j压在一起(用c[]记录b[j]对应的i值),然后就能用pair记录状态,比原来的结构体不知道高到哪里去了。
d[]的含义与前面几乎相同,只不过现在变成了b[a[i]]-b[a[i-1]]。也正因为有d,a[i]==1的情况需要特殊处理。
将题目按d排序后,存到e[]里,即e[i]表示排第i的是谁,与后缀数组中的sa[]类似。
由e[]得到f[],即谁排第几,与后缀数组中的rk[]类似。
没了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值