链接:https://www.nowcoder.com/acm/contest/143/H
来源:牛客网
间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
Kanade has an array a[1..n] , she define that an array b[1..m] is good if and only if it satisfy the following conditions:
-
1<=b[i]<=n
-
b[i]<b[i+1] for every i between 1 and m-1
-
a[b[i]] < a[b[i+1]] for every i between 1 and m-1
-
m>0
Now you need to find the k-th smallest lexicographically good array.
输入描述:
The first line has two integer n,k The second line has n integer a[i]
输出描述:
If there is no solution, just only output -1, else output two lines, the first line has an integer m, the second line has m integer b[i]
示例1
输入
复制
3 2 1 2 3
输出
复制
2 1 2
示例2
输入
复制
3 1000000000 1 2 3
输出
复制
-1
备注:
1<=n <= 5*10^5 1<=k<=10^(18) 1<=a[i]<=10^9
题意:给定一串长度为n的序列,求这个序列字典序第k小的上升子序列。
思路:一串序列会有很多不同的上升子序列,我们先考虑如何确定上升子序列,我们考虑逐位确定上升子序列的个数。对于每一位a[i]我们定义dp[i]是以a[i]开头的上升子序列的个数,那么
不明白的话可以拿样例来推一下:从后往前首先是3,dp[3]=1;然后是2,dp[2]等于dp[3]加上1,这个加上的1的上升子序列就是只有一个2的情况,dp[2]=1+1=2;然后dp[1]=1+dp[2]+dp[3]=4;
逐位确定完成了之后,从头往后遍历,如果k<=dp[i]的话说明本位是我们所求子序列中的一位,加入答案,然后k减一。如果k>dp[i]就说明本位开头的子序列不符合条件,k-=dp[i],本位不计入答案。
可以用树状数组逐位确定dp[i],首先要注意dp[i]是类似后缀和,所以树状数组更新和求和操作要反着来,此外因为序列长度n最长为5e5,那么极端情况下长度为5e5的序列是个严格递增序列的话那么dp大小肯定会比longlong大,那么为了防止爆longlong,我们知道k的最大值是1e18,所以求和以及更新操作的时候如果值超过了1e18置为1e18即可。(序列元素最大值为1e9,别忘了离散化)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e5+5;
int a[maxn],n,b[maxn],g[maxn];
ll k,c[maxn],dp[maxn];
int lowbit(int x)
{
return x&-x;
}
void add(int p,ll v)
{
while(p)
{
c[p]+=v;
if(c[p]>1e18) c[p]=1e18;
p-=lowbit(p);
}
}
ll sum(int p)
{
ll res=0;
while(p<=maxn)
{
res+=c[p];
if(res>1e18) res=1e18;
p+=lowbit(p);
}
return res;
}
int main()
{
scanf("%d %lld",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=a[i];
}
int p=unique(b+1,b+n+1)-b;
sort(b+1,b+p+1);
for(int i=1;i<=n;i++)
{
a[i]=lower_bound(b+1,b+p+1,a[i])-b;
}
dp[n]=1;
add(a[n],dp[n]);
for(int i=n-1;i>=1;i--)
{
dp[i]=sum(a[i]+1)+1;
add(a[i],dp[i]);
}
int cnt=0;
for(int i=1;i<=n;i++)
{
if(k==0) break;
if(a[i]>a[g[cnt]])
{
if(k<=dp[i]) k--,g[++cnt]=i;
else k-=dp[i];
}
}
if(k)
{
printf("-1\n");
return 0;
}
printf("%d\n",cnt);
for(int i=1;i<=cnt;i++)
printf("%d ",g[i]);
printf("\n");
}