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);
	}
}
上一篇:D - Strange Lunchbox


下一篇:[CF1539F] Strange Array (线段树)