2018牛客多校第五场 H、subseq(树状数组)

链接: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. 1<=b[i]<=n

  2. b[i]<b[i+1] for every i between 1 and m-1

  3. a[b[i]] < a[b[i+1]] for every i between 1 and m-1

  4. 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]开头的上升子序列的个数,那么dp[i]=1+\sum_{i+1}^nd[j](a[j]>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");
}

猜你喜欢

转载自blog.csdn.net/baymax520/article/details/81490708
今日推荐