题解:P13968 [VKOSHP 2024] Classics

双倍经验:P4309 [TJOI2013] 最长上升子序列话说凭啥这题是蓝,那题是紫。

分析

我们需要在每次插入时获得 LIS。

我们可以直接模拟得到所有插入操作之后的序列。

然后枚举每一个数。由于是按顺序插入,所以先处理小的,再处理大的,就不会影响答案。

\(pos_i\) 表示 \(i\)最终数组中的位置,维护 \(\max\limits_{i=1}^{pos_i-1}dp_i\),即 \(dp\) 数组中 \([1,pos_i)\) 的最大值即可。最后更新一下 \(dp_{pos_i}\) 就行了。

:::info[那我万一有一个 \(j\) 满足 \(j>i,pos_j<pos_i\) 怎么办]
这时候 \(j\) 还没有插入,没有影响。
:::

你可以使用 rope 或者支持分裂的平衡树维护。注意 rope 的下标是 0 开始的。

注意!线段树无法处理 \([1,1)\) 这种无效区间,所以记得特判 \(pos_i=1\)

AC record

#include<bits/extc++.h>
#define endl '\n'
using namespace std;
using namespace __gnu_cxx;
rope<int> r;
const int maxn=2e5+5;
struct T{
	struct node{
		int l,r,max;
	}t[maxn*4];
#define ls id<<1
#define rs id<<1|1
	void up(int id){t[id].max=max(t[ls].max,t[rs].max);}
	void build(int id,int l,int r){
		t[id].l=l,t[id].r=r;
		if(l==r){
			t[id].max=0;
			return;
		}
		int mid=(l+r)>>1;
		build(ls,l,mid);
		build(rs,mid+1,r);
		up(id);
	}
	int query(int id,int l,int r){
		if(t[id].l>r||t[id].r<l)return INT_MIN;
		if(l<=t[id].l&&t[id].r<=r)return t[id].max;
		return max(query(ls,l,r),query(rs,l,r));
	}
	void update(int id,int x,int val){
		if(t[id].l>x||t[id].r<x)return;
		if(t[id].l==t[id].r&&t[id].l==x){t[id].max=val;return;}
		update(ls,x,val);update(rs,x,val);
		up(id);
	}
}t;
int ans[maxn],pos[maxn];
int main(){
	int n;
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){int x;cin>>x;r.insert(x-1,i);}
    for(int i=0;i<n;i++)pos[r[i]]=i+1;
	t.build(1,1,n);
	for(int i=1;i<=n;i++){//枚举K
        if(pos[i]==1)ans[i]=1;//特判!
		else ans[i]=t.query(1,1,pos[i]-1)+1;//[1,pos_i)
		t.update(1,pos[i],ans[i]);
	}
	for(int i=1;i<=n;i++){
		ans[i]=max(ans[i],ans[i-1]);
		cout<<ans[i]<<endl;
	}
	return 0;
} 
posted @ 2025-12-31 20:25  zhangruixiang  阅读(2)  评论(0)    收藏  举报