Nanjing F. Greedy Sequence(线段树 - 单点修改 区间最大值查询)
作者:互联网
链接:https://nanti.jisuanke.com/t/41303
来源:The Preliminary Contest for ICPC Asia Nanjing 2019
题意:给我们一个序列(全排列中的某个序列),序列长度为n。现在需要我们用这个序列组成n个长度为n新序列,问每个序列中非零的数字共有多少个?这n个序列的条件就是:第 i 个序列的开头的元素为 i ,从第二个数开始选择的数字一定要比他的前一个数小,不够 n 个数就在序列的后面补零。选择数字的规则:当前数的最后一个数就是[pos[当前数]-k,pos[当前数]+k]这个区间内小于当前数的最大的那个数。
思路:刚开始我们维护一个所有元素都为 0 的线段树,对于每一个 i,我们都找到每个 < i 最大的值mmmax(题目要求的区间内),找到 < i的最大值得时候,我们就可以将 i 插入到线段树中(便于后面寻找最大值),那么这个序列能够选到的非零数的个数就是ans[i]=ans[mmmax]+1。
#include<bits/stdc++.h>
#define LNode x<<1
#define RNode x<<1|1
using namespace std;
const int Max_n=1e5+10;
int mmax[4*Max_n],a[Max_n],pos[Max_n],ans[Max_n];
void update(int x){
mmax[x]=max(mmax[LNode],mmax[RNode]);
}
void build(int l,int r,int x){//构建线段树(自顶向下)
mmax[x]=0;//刚开始全部为0
if(l==r){ return ; }
int mid=(l+r)>>1;
build(l,mid,LNode);
build(mid+1,r,RNode);
update(x);
//build(1,n,1);//从根开始向上建立线段树
}
//线段树查询(自顶向下)
int query(int A,int B,int l,int r,int x){
if(A<=l&&B>=r) return mmax[x];
int mid=(l+r)>>1,ans=0;
if(A<=mid) ans=max(ans,query(A,B,l,mid,LNode));//查询左子树
if(B>mid) ans=max(ans,query(A,B,mid+1,r,RNode));//查询右子树
return ans;
}
//p[i]的位置上修改为i
void change(int pos,int v,int l,int r,int x){///单点修改数据(log(n))
if(l==r) {mmax[x]=v; return ;}
int mid=(l+r)>>1;
if(pos<=mid) change(pos,v,l,mid,LNode);
else change(pos,v,mid+1,r,RNode);
update(x);
}
int main(){
int T;
scanf("%d",&T);
while(T--){
int n,k;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) ans[i]=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
pos[a[i]]=i;
}
build(1,n,1);
for(int i=1;i<=n;i++){
int A=max(1,pos[i]-k),B=min(n,pos[i]+k);
int mmmax=query(A,B,1,n,1);//对于第i个序列,找到<i的最大的数
change(pos[i],i,1,n,1);
ans[i]=ans[mmmax]+1;
//cout<<" pos[i]:"<<pos[i]<<" i:"<<i<<" max:"<<mmmax<<" ans[i]:"<<ans[i]<<endl;
}
for(int i=1;i<=n;i++) printf("%d%c",ans[i],i==n?'\n':' ');
}
return 0;
}
标签:Sequence,int,线段,mid,Nanjing,pos,Greedy,ans,序列 来源: https://blog.csdn.net/qq_42217376/article/details/100516257