Newcoder 143 H.subseq(BIT)

Description

给出一个长度为 n n n的序列 a 1 , . . . , a n a_1,...,a_n a1,...,an,定义序列 b 1 , . . . , b m b_1,...,b_m b1,...,bm是好的当且仅当以下条件成立:

1. 1 ≤ b i ≤ n , 1 ≤ i ≤ m 1\le b_i\le n,1\le i\le m 1bin,1im

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

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

求所有好的 b b b序列中字典序第 k k k小的

Input

第一行输入两个整数 n , k n,k n,k,之后输入 n n n个整数 a 1 , . . . , a n a_1,...,a_n a1,...,an

( 1 ≤ n ≤ 5 ⋅ 1 0 5 , 1 ≤ k ≤ 1 0 18 , 1 ≤ a i ≤ 1 0 9 ) (1\le n\le 5\cdot 10^5,1\le k\le 10^{18},1\le a_i\le10^9) (1n5105,1k1018,1ai109)

Output

输出字典序第 k k k小的好的 b b b序列,无解则输出 − 1 -1 1

Sample Input

3 2
1 2 3

Sample Output

2
1 2

Solution

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

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

余额充值