题意
给⼀个⻓度为
𝑛
的序列
𝑎
,你可以删去任意多个数
给⼀个⻓度为 𝑛 的序列 𝑎,你可以删去任意多个数
给⼀个⻓度为n的序列a,你可以删去任意多个数
最⼤化剩余序列中,数值等于位置标号的个数。
最⼤化剩余序列中,数值等于位置标号的个数。
最⼤化剩余序列中,数值等于位置标号的个数。
input
5
1 1 2 5 4
output(可变为1 2 5 4)
3
subtask
对于 50% 的数据,
n
≤
1
e
3
n\le1e3
n≤1e3 ;
对于 100% 的数据,
n
≤
5
e
5
n\le5e5
n≤5e5;
其中
a
i
≤
1
e
9
a_i\le1e9
ai≤1e9 ;
solution
首先发现贪心不可行,锁定dp,很容易想到一个线性dp的做法:
令状态 f i f_i fi 表示处理 1 1 1 到 i i i 这些数,并且处理后的序列以 a i a_i ai 结尾且数字 a i a_i ai 移动到位置 a i a_i ai 上时的最大答案。 其中由于任意 a i a_i ai的位置只能往前移动,所以只有满足 a i ≤ i a_i\le i ai≤i的状态才合法。
转移易得 f i = max ( f j + 1 ) f_i=\max(f_j+1) fi=max(fj+1)
其中转移条件为:
{ j < i a j < a i j − a j ≤ i − a i \left\{ \begin{array}{rcl} j<i\\ a_j<a_i\\ j-a_j\le i-a_i \ \end{array} \right. ⎩ ⎨ ⎧j<iaj<aij−aj≤i−ai
条件很好理解: f i f_i fi由于下标 i i i的递增只能从小于 a i a_i ai 的 a j a_j aj的状态转移来;而让 数字 a j a_j aj 移动到位置 a j a_j aj所需删数的数量不能超过 a i a_i ai的,即 j j j位置前删除的数不能超过 i i i位置前删除的数。
初态为 d p i = 1 ( a i ≤ i ) dp_i=1(a_i\le i) dpi=1(ai≤i),因为对于任意一个满足 a i ≤ i a_i\le i ai≤i的数都一定可以从位置 i i i 移动到位置 a i a_i ai
那么就有了一个朴素转移的 O ( n 2 ) O(n^2) O(n2)的dp,可以过掉subtask1
//50pts subtask1
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
int n,a[maxn],f[maxn],ans;
signed main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(a[i]<=i) f[i]=1,ans=1;
}
for(int i=1;i<=n;i++)
if(a[i]<=i)
for(int j=1;j<i;j++)
if(a[j]<a[i]&&a[i]-a[j]<=i-j)
f[i]=max(f[i],f[j]+1),ans=max(ans,f[i]);
cout<<ans<<endl;
return 0;
}
那么怎么优化呢,可以从转移条件入手
{ j < i a j < a i j − a j ≤ i − a i \left\{ \begin{array}{rcl} j<i\\ a_j<a_i\\ j-a_j\le i-a_i \ \end{array} \right. ⎩ ⎨ ⎧j<iaj<aij−aj≤i−ai
对于这种类似三维偏序的转移,可以用二分数据结构或者CDQ分治优化,可以做到 O ( n log 2 n ) O(n\log^2 n) O(nlog2n),虽然本题数据不能AC,这种优化方式还是很值得学习的。
进一步的,凭借 小学 学习的不等式,很容易得到后两个不等式是第一个的充分不必要条件,所以只需要考虑后两个不等式即可
{ a j < a i j − a j ≤ i − a i \left\{ \begin{array}{rcl} a_j<a_i\\ j-a_j\le i-a_i \ \end{array} \right. {aj<aij−aj≤i−ai
采用二维偏序的思想,按照 a i a_i ai 为关键字排序,这样一定满足在新序列从前向后转移时, a j < a i 且 j < i a_j<a_i且j<i aj<ai且j<i。又由于每次转移贡献为1,所以只要在新序列的顺序中以 ( i − a i ) (i-a_i) (i−ai) 为元素值求最长不降序列,其长度即为所求答案。
人尽皆知的,最长不降序列问题可以通过树状数组或者单调栈做到 O ( n log n ) O(n\log n) O(nlogn),就能AC此题。
//100pts subtask2
#include <bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
const int INF=0x3f3f3f3f;
struct node{int p, a,val;}a[maxn];
int n,m,h[maxn];
inline bool cmp(const node &A,const node &B)
{
if(A.a==B.a) return A.p>B.p;
else return A.a<B.a;
}
signed main()
{
memset(h, 0x3f, sizeof(h));
cin>>n;int x;
for(int i=1;i<=n;i++)
cin>>x,a[i]={i,x,i-x};
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
if(a[i].val>=0)
h[(upper_bound(h+1,h+n+1,a[i].val)-h)]=a[i].val;
for (int i=n;i>=0;i--)
if(h[i]<INF)
return cout<<i<<endl,0;
cout<<0<<endl;
return 0;
}