Newcoder 143 H.subseq(BIT)

本文介绍一种高效算法,用于寻找给定序列中字典序第K小的好序列。通过动态规划与树状数组相结合的方式,在O(nlogn)的时间复杂度内解决问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

给出一个长度为nnn的序列a1,...,ana_1,...,a_na1,...,an,定义序列b1,...,bmb_1,...,b_mb1,...,bm是好的当且仅当以下条件成立:

1.1≤bi≤n,1≤i≤m1\le b_i\le n,1\le i\le m1bin,1im

2.bi&lt;bi+1,1≤i&lt;mb_i&lt;b_{i+1},1\le i&lt;mbi<bi+1,1i<m

3.abi&lt;abi+1,1≤i&lt;ma_{b_i}&lt;a_{b_{i+1}},1\le i&lt;mabi<abi+1,1i<m

求所有好的bbb序列中字典序第kkk小的

Input

第一行输入两个整数n,kn,kn,k,之后输入nnn个整数a1,...,ana_1,...,a_na1,...,an

(1≤n≤5⋅105,1≤k≤1018,1≤ai≤109)(1\le n\le 5\cdot 10^5,1\le k\le 10^{18},1\le a_i\le10^9)(1n5105,1k1018,1ai109)

Output

输出字典序第kkk小的好的bbb序列,无解则输出−1-11

Sample Input

3 2
1 2 3

Sample Output

2
1 2

Solution

dp[i]dp[i]dp[i]表示以iii开头的满足条件的bbb序列个数,那么有dp[i]=1+∑i&lt;j,ai&lt;ajdp[j]dp[i]=1+\sum\limits_{i&lt;j,a_i&lt;a_j}dp[j]dp[i]=1+i<j,ai<ajdp[j],用树状数组维护dp[i]dp[i]dp[i]的值,从后往前转移,每次把dp[i]dp[i]dp[i]根据aia_iai的值插入树状数组中,即可O(nlogn)O(nlogn)O(nlogn)得到dpdpdp序列

之后从前往后考虑字典序第kkk小的bbb序列,假设第一位放111,那么有dp[1]dp[1]dp[1]种方案数,若k≤dp[1]k\le dp[1]kdp[1],说明第一位确实是111,进而考虑第二位的取值(注意:1.每次考虑当前位的取值必然要大于已经确定的前一位取值;2.由于确定完当前位后,后面的取值有空和非空两种情况,此时要将kkk减一来去掉后面为空的情况,减完后若kkk000说明后面确实为空,否则继续考虑后面位的取值),若k&gt;dp[1]k&gt;dp[1]k>dp[1],说明第一位不是111,那么k=k−dp[1]k=k-dp[1]k=kdp[1],表示有dp[1]dp[1]dp[1]种字典序小的方案已经考虑了,下面考虑其他值放在第一位,以此类推,若考虑完所有元素kkk仍然非000则无解,否则已经确定的序列即为答案,时间复杂度O(n)O(n)O(n)

Code

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
ll INF=2e18;
#define maxn 500005
struct BIT 
{
	#define lowbit(x) (x&(-x))
	ll b[maxn],n;
	void init(int _n)
	{
		n=_n;
		for(int i=1;i<=n;i++)b[i]=0;
	}
	void update(int x,ll v)
	{
		while(x<=n)
		{
			b[x]+=v;
			b[x]=min(b[x],INF);
			x+=lowbit(x);
		}
	}
	ll query(int x)
	{
		ll ans=0;
		while(x)
		{
			ans+=b[x];
			ans=min(ans,INF);
			x-=lowbit(x);
		}
		return ans;
	}
}bit;
int n,a[maxn],h[maxn];
ll k,dp[maxn];
vector<int>ans;
int main()
{
	scanf("%d%lld",&n,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		h[i-1]=a[i];
	}
	sort(h,h+n);
	int m=unique(h,h+n)-h;
	bit.init(m);
	for(int i=n;i>=1;i--)
	{
		a[i]=m+1-(lower_bound(h,h+m,a[i])-h+1);
		dp[i]=bit.query(a[i]-1)+1;
		dp[i]=min(dp[i],INF);
		bit.update(a[i],dp[i]);
	}
	for(int i=1;i<=n;i++)
	{
		if(ans.size()>0&&a[i]>=a[ans[ans.size()-1]])continue;
		if(k==0)break;
		if(dp[i]>=k)
		{
			ans.push_back(i);
			k--;
		}
		else k-=dp[i];
	}
	if(k)printf("-1\n");
	else
	{
		printf("%d\n",ans.size());
		for(int i=0;i<ans.size();i++)
			printf("%d%c",ans[i],i==ans.size()-1?'\n':' ');
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值