Atcoder [ARC165B] Sliding Window Sort 2 题解

Description

给定一个长度为 \(n\) 的整数序列和一个整数 \(k\),你需要进行一次操作,任意选择的一个长度为 \(k\) 的区间并将该区间按升序排序。求能够得到的字典序最大的序列。


Solution

首先,由于是按升序排序,得到的新序列的字典序一定不会比原序列大。当且仅当原序列中存在一个长度大于等于 \(k\) 的单调递增序列时新序列和原序列的字典序相等,这种情况直接输出原序列即可。

如果不存在这样的单调递增序列,根据贪心策略,显然尽量选靠后的区间进行操作是更优的。这是因为如果选较为靠前的区间 \([l,r]\),那么 a[l] 的位置可能被更小的数代替,一定不会比选后面的区间更优。

但是选最靠后的区间 \([n-k+1,n]\) 仍然不一定是最优策略。考虑一个区间 \([l,r]\) 满足 \(r\in[n-k+1,r-1]\),且区间 \([l,n-k]\) 是单调递增的,且 \([l,n-k]\) 的最大值小于等于 \([n-k+1,r]\) 的最小值。显然 \([l,n-k]\) 这部分是不会改变的。对于 \([n-k+1,r]\) 这一部分,如果 a[r+1] 大于等于这一段的最大值,那么选 \([n-k+1,r]\)\([n-k+1,r+1]\)是等价的,不会改变结果。如果a[r+1] 小于这一段的最大值,那么排序后 a[r+1]应该在的位置被一个更大的数代替了,排序后的序列字典序更大了。因此,只要区间 \([l,n-k]\) 是单调递增的,那么让 \(r\) 更接近 \(n-k+1\) 一定更优。可以倒序枚举 \(l\in[\min(n-2*k+2,1),n-k]\),则\(r=l+k-1\)。如果不满足 \([l,n-k]\) 单调递增则直接 break。若满足\([l,n-k]\) 的最大值小于等于 \([n-k+1,r]\) 的最小值,就更新答案选取的区间。

由于单调递增,\([l,n-k]\) 的最大值实际上就是 a[n-k]。而对于 \([n-k+1,r]\) 的最小值,可以用线段树或 ST 表求出。

时间复杂度 \(O(n\log n)\)


Code

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int a[N];
struct trnode{
	int l,r,lc,rc,c;
}tr[N*4];
int trlen;
void build_tree(int l,int r)
{
	int now=++trlen;
	tr[now]={l,r,-1,-1,0};
	if(l==r) tr[now].c=a[l];
	else
	{
		int mid=(l+r)/2;
		tr[now].lc=trlen+1;build_tree(l,mid);
		tr[now].rc=trlen+1;build_tree(mid+1,r);
		tr[now].c=min(tr[tr[now].lc].c,tr[tr[now].rc].c);
	}
}
int get_min(int now,int l,int r)
{
	if(tr[now].l==l&&tr[now].r==r) return tr[now].c;
	int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)/2;
	if(r<=mid) return get_min(lc,l,r);
	else if(l>=mid+1) return get_min(rc,l,r);
	else return min(get_min(lc,l,mid),get_min(rc,mid+1,r));
}
int main()
{
	int n,k;
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	if(n==k)
	{
		sort(a+1,a+n+1);
		for(int i=1;i<=n;i++) printf("%d ",a[i]);
		exit(0);
	}
	int cnt=1;
	for(int i=2;i<=n;i++)
	{
		if(cnt>=k)
		{
			for(int i=1;i<=n;i++) printf("%d ",a[i]);
			exit(0);
		}
		if(a[i]>=a[i-1]) cnt++;
		else cnt=1;
	}
	build_tree(1,n);
	int ans=n-k+1;
	for(int i=n-k;i+k-1>=n-k+1&&i>=1;i--)
	{
		if(a[n-k]<=get_min(1,n-k+1,i+k-1)) ans=i;
		if(a[i-1]>a[i]) break;
	}
	sort(a+ans,a+ans+k);
	for(int i=1;i<=n;i++) printf("%d ",a[i]);
	return 0;
}
posted @ 2024-01-28 12:44  __Star_Sky  阅读(38)  评论(0)    收藏  举报