F - Prefix LIS Query
题目链接:https://atcoder.jp/contests/abc393/tasks/abc393_f
题意:
给定一个序列,q个询问
每个循环给定一个R和X
请找出在[1,R]序列前缀范围的且最大值不超过X的LIS
思路:
对于所有询问考虑离线查询
规定f[i]为长度为i的LIS的最小结尾值
(对于以下的最优LIS实际上就是以i作结尾的线性dp)
从头开始维护f数组
需要用到nlogn的LIS做法
(即使用辅助数组b,来记录当前状态下的最优最长上升子序列)
对于遍历到的a[j],如果大于最优LIS的最大值,则pushback,并且表示当前最大长度的LIS的最小结尾值为a[j]
反之,如果小于最优LIS的最小值,那么二分更新序列第一个大于等于它的值,并且更新对应长度的最小结尾值
由于f数组记录最小结尾值,且易知f数组是单调递增的。所以可以二分查找f值第一个小于等于x的下标,便是该次查询的答案
int f[maxn];
void solve(){
int n,len=1;
int a[maxn];
vector<int>b;
int q;
cin>>n>>q;
vector<pair<pii,int>>s;
rep(i,1,n) {
cin>>a[i];
}
rep(i,1,q){
int r,x;cin>>r>>x;
s.pb({{r,x},i});
}
sort(s.begin(),s.end());
b.pb(a[1]);
int st=2;
f[1]=a[1];
vector<int>ans(q+1);
int c=1;
for(int i=0;i<q;i++){
int r=s[i].fi.fi,x=s[i].fi.se,index=s[i].se;
for(int j=st;j<=r;j++){
if(a[j]>b.back()){
b.pb(a[j]);f[++c]=a[j];
}else{
int pos=lower_bound(b.begin(),b.end(),a[j])-b.begin();
b[pos]=a[j];
f[pos+1]=a[j];
}
}
int lt=0,rt=b.size(),res=0;
while(lt<=rt){
int mid=lt+rt>>1;
if(f[mid]>x){
rt=mid-1;
}else{
res=mid;
lt=mid+1;
}
}
st=r+1;
ans[index]=res;
}
rep(i,1,q){
cout<<ans[i]<<endl;
}
}

浙公网安备 33010602011771号