如果本文出锅,请评论或私信提醒这个蒟蒻修改!
题意
题目给的很清楚了,不多说。
分析
看到题目,因为在dp题单里,所以一眼是个dp,我们先想朴素算法,可以发现,如果设 \(f_{i,j}\) 表示前 \(i\) 个数中删掉 \(j\) 个所能得到的最大结果,若 \(a_i=i\),则 \(f_{i,j}=f_{i-1,j}+1\);否则,可以直接从 \(f_{i-1,j-1}\) 转移。
可以很快地打出一个 \(O(n^2)\) 的暴力。
f[1][0]=(a[1]==1);
for(ll i=2;i<=n;++i)f[i][0]=f[i-1][0]+(a[i]==i);
for(ll i=1;i<=n;++i){
for(ll j=1;j<=i;++j){
f[i][j]=max(f[i-1][j]+(a[i]==i-j),f[i-1][j-1]);
}
}
但是由于 \(1\le n\le 2\times 10^5\),所以它会 TLE+MLE。
第一维的 \(i\) 不可能优化掉。
看第二维 \(j\),经过思考后可以发现,因为空间复杂度应是 \(O(n)\) 级别的,就可以设 \(f_{i,0/1}\) 表示第 \(i\) 个数是否归到 \(a_i\) 的位置上的最大结果。思再想一想,可发现应设 \(f_i\) 表示 \(i\) 归位的最大结果,因为求 \(f_{i,0}\) 没什么意义。
对此,可得 \(f_i=\max(f_j)+1\quad(a_j<a_i,j-a_j\le i-a_i)\),然后可用树状数组维护二维偏序,不会的看这里(能做这题的应该都会吧)。
Code
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
ll n,a[2000005],c[2000005],m=1e6;
inline ll lowbit(ll x){return x&-x;}
inline void update(ll x,ll val){
while(x<=m){
c[x]=max(c[x],val);
x+=lowbit(x);
}
}
inline ll ask(ll x){
ll res=0;
while(x){
res=max(res,c[x]);
x-=lowbit(x);
}
return res;
}//树状数组维护二维偏序
vector<ll>cnt[2000005];
signed main(){
cin>>n;
for(ll i=1;i<=n;++i){
cin>>a[i];
if(i>=a[i]){
cnt[i-a[i]].push_back(a[i]);//预处理一下可能的i-a[i]
}
}
for(ll i=0;i<=m;++i){
for(ll j:cnt[i]){
update(j,ask(j-1)+1);//更新
}
}
cout<<ask(m);
return 0;
}
标签:2000005,le,ll,long,CF1575L,Deconstruction,Array,dp
From: https://www.cnblogs.com/run-away/p/18030325