「AGC038B Sorting a Segment」

题目大意

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

分析

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

  1. 这次排序并没有改变这个序列,也就是说这次排序的区间本来就是有序的.这种情况下的所有状况都只会产生一种结果.
  2. 假如某次排序的左端点为 (l) 右端点为 (l+k-1),那么这次排序可能与 (l-1sim 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;
}
原文地址:https://www.cnblogs.com/Sxy_Limit/p/13904189.html