一、题目
二、解法
( t oneindark) 真的离谱,这种题都能切呢?(3300) 的题都能切呢?!
首先我们应用 (01) 原则,(forall i),我们把前 (i) 小的数变成 (0),剩下的数变成 (1),然后对这个数列排序,所有排序次数取最大值就是答案。每次得到的条件是前 (i) 小的数被排好序了,所以大家都被排好序了。
结论:(01) 序列的排序次数为某个在 (0) 前面的 (1),其前面 (1) 的个数(+)后面 (0) 的个数(+[)这是否是偶数位置(])的最大值。
考虑 (f(i,j)) 表示第 (i) 个 (1) 越过第 (j) 个 (0) 的排序次数,转移:
[f(i,j)=max(f(i+1,j),f(i,j-1))+1
]
这其实相当于图上求最长链,那么我们考虑每个叶子的最长链即可,每次可以向左使 (i-1),向右使 (j+1),初值和位置 (i) 的奇偶性有关,然后加上前面 (1) 的个数和后面 (0) 的个数即可。
因为这个函数定义在 (1) 上所以只能在 (1) 位置取最值,因为只有在 (0) 前面的 (1) 有定义所以只能考虑它。
用线段树把上面的结论模拟出来即可,时间复杂度 (O(nlog n))
三、总结
排序问题可以转 (01) 序列,注意 (01) 原则的应用即可。
本题反复取 (max) 也是有趣之处,主要思想是把困难的问题拆成若干独立子问题。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 200005;
const int inf = 0x3f3f3f3f;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int T,n,ans,b[M],mx[4*M],fl[4*M];
void jzm(int i,int c)
{
mx[i]+=c;fl[i]+=c;
}
void down(int i)
{
jzm(i<<1,fl[i]);
jzm(i<<1|1,fl[i]);
fl[i]=0;
}
void build(int i,int l,int r)
{
mx[i]=fl[i]=0;
if(l==r)
{
mx[i]=-inf+(l%2==0)+l-1;
return ;
}
int mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
mx[i]=max(mx[i<<1],mx[i<<1|1]);
}
void add(int i,int l,int r,int L,int R,int c)
{
if(L>r || l>R) return ;
if(L<=l && r<=R) {jzm(i,c);return ;}
int mid=(l+r)>>1;down(i);
add(i<<1,l,mid,L,R,c);
add(i<<1|1,mid+1,r,L,R,c);
mx[i]=max(mx[i<<1],mx[i<<1|1]);
}
signed main()
{
T=read();
while(T--)
{
n=read();ans=0;
for(int i=1;i<=n;i++)
b[read()]=i;
build(1,1,n);
for(int i=1,j=1;i<=n;i++)//1->0
{
int x=b[i];
for(;j<=x;j++) add(1,1,n,j,j,inf);
add(1,1,n,x,x,-inf);
add(1,1,n,x,n,-1);
add(1,1,n,1,x-1,1);
ans=max(ans,mx[1]);
}
printf("%d
",ans);
}
}