CF1558F Strange Sort
一、题目
二、解法
\(\tt 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(n\log 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\n",ans);
}
}