牛客网暑期ACM多校训练营(第五场)H-subseq 树状数组
subseq
链接:https://www.nowcoder.com/acm/contest/143/H
来源:牛客网
题目描述
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
题意:给出一个数组a,求的一个第k小且符合b[i]<b[i+1] a[b[i]]<b[b[i+1]]的序列
思路:我们可以考虑每个点的贡献 如果 a序列为 1 2 3
我们可以写出 1 ,1 2,1 3, 1 2 3 四种以1开头的符合题意的序列b
分析下样例1:我们能的到如下序列b, 1, 1 2, 1 2 3,1 3, 2 , 2 3,3 字典序第2小 显然是1 2我们即可输出 1 2
考虑我们怎么维护树状数组,我们离散化后,我们维护的是这个点能给其他点添加序列 比如3 能给1添加两次贡献,能 2添加1次 给3添加1次
我们从a[n]遍历到a[1],我插入前需要查询此点的贡献。
继续分析样例:
3离散后也是,3,我们查询比3大的是否有贡献,3为第一个插入的数 贡献为它自己1 插入到树状数组
继续 2离散后是2,我们查询比2大的是否有贡献,显然3之前我们已经插入了 ,此时2的贡献为1+1(3的贡献 插入到树状数组
最后 1离散后是1,我们查询比1大的是否有贡献,显然2和3我们之前已经插入了,此时1的贡献为1+2(2的贡献)+1(3的贡献) 然后插入进去
然后我们只需要慢慢求下标就好了,k>f[i](a[i]的贡献)我们就不需要用到a[i]这个点,k<=f[i]时我们就只要慢慢求出和a[i]构成的序列即可
在维护的时候如果贡献大于1e18我们就只需要讲贡献改为1e18就行 k的取值最大1e18
#include<bits/stdc++.h> #define ll long long using namespace std; const int maxn=6e5+10; const long long inf=1e18; ll a[maxn],f[maxn],tree[maxn],N; vector<ll> v; int getid(ll x){return lower_bound(v.begin(),v.end(),x)-v.begin()+1;} int lowbit(int x){return x&(-x);} void add(int x,ll val) { for(; x; x-=lowbit(x)) { tree[x]+=val; if(tree[x]>=inf) tree[x]=inf; } } ll query(int x) { ll ans=0; for(; x<=N; x+=lowbit(x)) { ans+=tree[x]; if(ans>=inf) ans=inf; } return ans; } int main() { int n; ll k; scanf("%d %lld",&n,&k); for(int i=1; i<=n; i++) { scanf("%lld",&a[i]); v.push_back(a[i]); } sort(v.begin(),v.end()); v.erase(unique(v.begin(),v.end()),v.end()); N=v.size(); for(int i=n; i>=1; i--) { f[i]=query(getid(a[i])+1)+1; add(getid(a[i]),f[i]); } ll last=0; vector<int> V; for(int i=1; i<=n; i++) { if(k==0) break; if(a[i]>last) { if(k<=f[i]) { k--; V.push_back(i); last=a[i]; } else { k-=f[i]; } } } if(k) return 0*puts("-1"); printf("%d\n",V.size()); for(int i=0; i<V.size(); i++) { printf("%d%c",V[i]," \n"[i==V.size()-1]); } return 0; }
PS:摸鱼怪的博客分享,欢迎感谢各路大牛的指点~