题目链接:Click here~~
题意:
给一个序列 {An} 和一个双向队列 D,每次可以向 D 的队首或队尾插入一个元素,且 D 要保证单调不减,问如何使最后队列的长度最长。
解题思路:
先考虑单调递增的情况,对于 D 所能够构成的每一种序列,都可以看做以某个元素 Ax 为起始元素所形成的单增序列 Px 和单减序列 Qx 合成的。
由于除了 Ax, Px 和 Qx 不会有其他交集,所以一定有 max( |D| ) <= max( |Px| ) + max( |Qx| ) - 1。
所以,只要对于每个Ax,都能快速得到它相应的 max( |Px| ) 和 max( |Qx| ),问题就可以解决了。
回顾求单调最长序列的过程,我们每次更新的时候,其实都是相当于把 Ai 插到了以 Ai 为末尾元素能够构成的最长单调序列的长度那里。
然后,我们求最长序列时从 n~1 逆向循环,就可以“负负得正”,将末尾元素变成起始元素了。
此题还需要考虑元素相同的情况,于是我们插入 Ai 时,判断 Ai 是否和长度比它少 1 的末尾元素相等,如果相等,则 cnt[Ai]++。
最后总序列中 Ax 的个数,应该等于 max( cnt(Ax) ∈ Px , cnt(Ax) ∈ Qx )。
小技巧:求单调递减序列比较麻烦(重载cmp函数 || 不能用pair),所以可以将元素全取相反数,然后继续求递增。
目前代码有bug。慎入。
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5;
pair<int,int> dp1[N],dp2[N];
int a[N],top1,top2;
int main()
{
int T,n;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
dp1[top1=1] = make_pair(a[n],1);
dp2[top2=1] = make_pair(-a[n],1);
int ans = 1;
for(int i=n-1;i>=1;i--)
{
int j1 = upper_bound(dp1+1,dp1+1+top1,make_pair(a[i],N)) - dp1;
if(j1 == top1 + 1)
top1++;
int cnt1 = a[i] == dp1[j1-1].first ? dp1[j1-1].second + 1 : 1;
dp1[j1] = make_pair(a[i],cnt1);
int j2 = upper_bound(dp2+1,dp2+1+top2,make_pair(-a[i],N)) - dp2;
if(j2 == top2 + 1)
top2++;
int cnt2 = -a[i] == dp2[j2-1].first ? dp2[j2-1].second + 1 : 1;
dp2[j2] = make_pair(-a[i],cnt2);
ans = max(ans,j1+j2-min(cnt1,cnt2));
}
printf("%d\n",ans);
}
return 0;
}