链接:https://www.nowcoder.com/acm/contest/143/H
时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 262144K,其他语言524288K 64bit IO Format: %lld
题目描述
Kanade has an array
, she define that an array
is good if and only if it satisfy the following conditions:
for every i between
and
for every i between
and
Now you need to find the k-th smallest lexicographically good array.
输入描述:
The first line has two integer
The second line has
integer
输出描述:
If there is no solution, just only output -1, else output two lines, the first line has an integer
, the second line has
integer
示例1
输入
3 2
1 2 3
输出
2
1 2
示例2
输入
3 1000000000
1 2 3
输出
-1
备注:
思路:题面很简洁。若a数组中有一个上升子序列,则b数组储存的是这个序列的下标。显然a数组有很多个不同的上升子序列,则b数组也有很多种。题目就是要你求出这些可能的b数组中,字典序第k小的是什么。
对于任意一个
,我们定义
是以
为起点的上升子序列的个数。
则
其中
代表只有
这一个数的序列。
这过程可以用线段树累计区间和。
(因为d数组会爆long long ,所以不知道怎么用BIT处理。。。)
现在我们知道了以每个 为起点的上升子序列的个数。
因为要保证答案是第k小,所以我们从下标最小的
开始枚举起点.
若
,那以
为起点满足条件,把
的下标加入答案,
。
然后往后找第一个
,判断
是否大于等于
。
若小于
,则
(因为这些序列不满足条件)。
若大于
则把
加入答案
…….
以此类推,直到
。
#include<bits/stdc++.h>
using namespace std;
const int MAX=5e5+10;
const int MOD=998244353;
const long long INF=1e18;
typedef long long ll;
vector<int>v;
int g(int x){return lower_bound(v.begin(),v.end(),x)-v.begin()+1;}
struct lenka
{
int l,r;
ll sum;
}A[MAX<<2];
void build(int k,int l,int r)
{
A[k].l=l,A[k].r=r;
A[k].sum=0;
if(l==r)return;
build(2*k,l,(l+r)/2);
build(2*k+1,(l+r)/2+1,r);
}
void add(int k,int x,ll y)
{
if(x==A[k].l&&x==A[k].r){A[k].sum=min(A[k].sum+y,INF);return;}
if(x<=A[2*k].r)add(2*k,x,y);
else add(2*k+1,x,y);
A[k].sum=min(INF,A[2*k].sum+A[2*k+1].sum);//防爆long long
}
ll ask(int k,int x,int y)
{
if(x>y)return 0;
if(x==A[k].l&&y==A[k].r)return A[k].sum;
if(y<=A[2*k].r)return ask(2*k,x,y);
if(x>=A[2*k+1].l)return ask(2*k+1,x,y);
return min(ask(2*k,x,A[2*k].r)+ask(2*k+1,A[2*k+1].l,y),INF);
}
int a[MAX];
ll d[MAX];
int main()
{
ll n,k;
cin>>n>>k;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)v.push_back(a[i]);
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());//将a数组中的值离散化
build(1,1,v.size());
for(int i=n;i>=1;i--)
{
ll x=ask(1,g(a[i])+1,v.size());//线段树查询并更新d数组
d[i]=min(INF,x+1);
add(1,g(a[i]),d[i]);
}
int st=-1;
for(int i=1;i<=n;i++)//找到满足序列个数>=k的起点
{
if(k>d[i])k-=d[i];
else
{
st=i;
break;
}
}
if(st==-1)puts("-1");
else
{
v.clear();
while(k)
{
v.push_back(st);
k--;
for(int j=st+1;j<=n;j++)
{
if(a[j]<=a[st])continue;
if(k>d[j])k-=d[j];
else
{
st=j;
break;
}
}
}
cout<<v.size()<<endl;
for(int i=0;i<v.size();i++)printf("%d%c",v[i],i==v.size()-1?'\n':' ');
}
return 0;
}