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;
}

浙公网安备 33010602011771号