[ABC352D] Permutation Subsequence(线段树,维护区间最大小值)
题目来源:https://www.luogu.com.cn/problem/AT_abc352_d
//
题意:“这sb题,题意看了很久很久~,才看懂,不要欺负我语文撇行不行”。 给你1~n的无序排列,从n个数中找出k个数字,要求这k个数字有序排列后都是连续的,找到k个数字中在原来排列的下标极差,求出所有组合的最小极差。
//样例:
n=10,k=5
10 1 6 8 7 2 5 9 3 4
其中一种组合是连续数字的5到9,最大的下标是8(数字9),最小的下标是3(数字6),极差就是5。
//
思路:对n个数字进行排序后得到:
数字:1 2 3 4 5 6 7 8 9 10
下标:2 6 9 10 7 3 5 4 8 1
这n个数字连续取k个,都是符合条件的(题目中的下标递增,nm的怎么取,只要是连续的k个数字,肯定都是下标递增的啊),然后坐标极差就是k个数字中最大下标减去最小下标。
直接把这n个排好序的数字的下标仍在线段树里面,然会枚举1到n-k+1,找到{i,i+1,i+2...i+k-1}里面的两个最值,得出极差,然后枚举取最小的就得出答案了。
//题解:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+9;
int n,k;
pair<int,int>arr[N];
vector<pair<int,int>>tree(4*N,pair<int,int>(0,0));//first维护最大值,second维护最小值
int lp(int p) {return p<<1;}
int rp(int p) {return p<<1 |1;}
void pushup(int p){
tree[p].first=max(tree[lp(p)].first,tree[rp(p)].first);
tree[p].second=min(tree[lp(p)].second,tree[rp(p)].second);
}
void build(int s,int t,int p){
if(s==t){
tree[p].first=arr[s].second;
tree[p].second=arr[s].second;
return;
}
int m=(s+t)>>1;
build(s,m,lp(p)),build(m+1,t,rp(p));
pushup(p);
}
int query_Min(int l,int r,int s,int t,int p){
if(l<=s && r>=t){
return tree[p].second;
}
int m=(s+t)>>1,Min1=N,Min2=N;
if(l<=m) { Min1=query_Min(l,r,s,m,lp(p));}
if(r>=m+1) {Min2=query_Min(l,r,m+1,t,rp(p));}
return min(Min1,Min2);
}
int query_Max(int l,int r,int s,int t,int p){
if(l<=s && r>=t){
return tree[p].first;
}
int m=(s+t)>>1,Max1=-N,Max2=-N;
if(l<=m) { Max1=query_Max(l,r,s,m,lp(p));}
if(r>=m+1) {Max2=query_Max(l,r,m+1,t,rp(p));}
return max(Max1,Max2);
}
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>arr[i].first;
arr[i].second=i;
}
sort(arr+1,arr+1+n);
build(1,n,1);//tree[]来维护arr.second的最值
int t1,t2,Min=N;
for(int i=1;i<=n-k+1;i++){
// cout<<query(i,i+k-1,1,n,1)<<'\n';
t1=query_Max(i,i+k-1,1,n,1);
t2=query_Min(i,i+k-1,1,n,1);
Min=min(Min,t1-t2);
}
cout<<Min<<'\n';
return 0;
}
浙公网安备 33010602011771号