专栏文章

题解:P13968 [VKOSHP 2024] Classics

P13968题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mjxqsh8j
此快照首次捕获于
2026/01/03 11:27
2 个月前
此快照最后确认于
2026/01/03 11:27
2 个月前
查看原文
双倍经验:P4309 [TJOI2013] 最长上升子序列话说凭啥这题是蓝,那题是紫。

分析

我们需要在每次插入时获得 LIS。
我们可以直接模拟得到所有插入操作之后的序列。
然后枚举每一个数。由于是按顺序插入,所以先处理小的,再处理大的,就不会影响答案。
posipos_i 表示 ii最终数组中的位置,维护 maxi=1posi1dpi\max\limits_{i=1}^{pos_i-1}dp_i,即 dpdp 数组中 [1,posi)[1,pos_i) 的最大值即可。最后更新一下 dpposidp_{pos_i} 就行了。
那我万一有一个 jj 满足 j>i,posj<posij>i,pos_j<pos_i 怎么办
这时候 jj 还没有插入,没有影响。
你可以使用 rope 或者支持分裂的平衡树维护。注意 rope 的下标是 0 开始的。
注意!线段树无法处理 [1,1)[1,1) 这种无效区间,所以记得特判 posi=1pos_i=1
CPP
#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;
} 

评论

0 条评论,欢迎与作者交流。

正在加载评论...