「AGC038B Sorting a Segment」

题目大意

给出一个 \(0\sim n-1\) 的排列,每次操作会将一个长度为 \(k\) 的子串从小到大排序,问经过一次操作后会产生多少种不同的排列.

分析

显然有 \(n-k+1\) 种排序的方式,但是在这当中可能会存在一些重复的,经过一些简单的分析不难发现重复只有两种情况:

  1. 这次排序并没有改变这个序列,也就是说这次排序的区间本来就是有序的.这种情况下的所有状况都只会产生一种结果.
  2. 假如某次排序的左端点为 \(l\) 右端点为 \(l+k-1\),那么这次排序可能与 \(l-1\sim l+k-2\) 这次排序相同,且相同的条件为 \(l+k-1\) 位置上的数比 \([l,l+k-2]\) 中的数都要大(这样在排序后才不会改变位置),\(l-1\) 位置上的数要比 \([l,l+k-2]\) 都要小.因为区间的长度是固定的,显然可以单调队列解决.

代码

#include<bits/stdc++.h>
#define REP(i,first,last) for(int i=first;i<=last;++i)
#define DOW(i,first,last) for(int i=first;last<=i;--i)
//宏定义模板
namespace IO
//快读模板
using namespace IO;
using namespace std;
const int MAXN=2e5+5;
int n,k;
int arr[MAXN];
int que[MAXN];
bool maxr[MAXN],minl[MAXN];
int order[MAXN];
int main()
{
	Read(n,k);
	REP(i,1,n)
	{
		Read(arr[i]);
	}
	int head=0,tail=0;
	REP(i,1,n)//单调队列处理长度为 k 的区间的最大值和最小值
	{
		while(head<=tail&&arr[que[tail]]<arr[i])
		{
			--tail;
		}
		que[++tail]=i;
		while(head<=tail&&k<i-que[head])
		{
			++head;
		}
		maxr[i]=arr[que[head]]<=arr[i];
	}
	head=0;
	tail=0;
	DOW(i,n,1)
	{
		while(head<=tail&&arr[i]<arr[que[tail]])
		{
			--tail;
		}
		que[++tail]=i;
		while(head<=tail&&k<que[head]-i)
		{
			++head;
		}
		minl[i]=arr[i]<=arr[que[head]];
	}
	order[1]=1;
	int answer=n-k+1;
	bool flag=1;
	REP(i,2,n)//判断第一种情况
	{
		order[i]=arr[i-1]<arr[i]?order[i-1]+1:1;
		if(k<=order[i])
		{
			answer+=flag;//所有的第一种情况总共会产生一个贡献
			flag=0;
			--answer;
		}
	}
	REP(i,k+1,n)
	{
		if(order[i]<k&&order[i-1]<k&&minl[i-k]&&maxr[i])//在不满足第一种的情况下判断第二种情况
		{
			--answer;
		}
	}
	Writeln(answer);
	return 0;
}
posted @ 2020-10-30 20:50  SxyLimit  阅读(104)  评论(0编辑  收藏  举报