「AGC038B Sorting a Segment」
题目大意
给出一个 \(0\sim n-1\) 的排列,每次操作会将一个长度为 \(k\) 的子串从小到大排序,问经过一次操作后会产生多少种不同的排列.
分析
显然有 \(n-k+1\) 种排序的方式,但是在这当中可能会存在一些重复的,经过一些简单的分析不难发现重复只有两种情况:
- 这次排序并没有改变这个序列,也就是说这次排序的区间本来就是有序的.这种情况下的所有状况都只会产生一种结果.
- 假如某次排序的左端点为 \(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;
}