51nod 1364 线段树
来源:互联网 发布:猫小萱淘宝 编辑:程序博客网 时间:2024/06/09 16:56
给出一个1至N的排列,允许你做不超过K次操作,每次操作可以将相邻的两个数交换,问能够得到的字典序最大的排列是什么?
例如:N = 5, {1 2 3 4 5},k = 6,在6次交换后,能够得到的字典序最大的排列为{5 3 1 2 4}。
解题:
《System Messag 的题解》
这个题目明显是要贪心的。从前往后每次看看能不能拿一个大的放到当前数字的前面,而且要拿尽可能大的。这样字典序才会变成最大。
原数组设为a[i]
这个过程可以用线段树来维护。先按照原数组建立线段树。树结点中记录最大值,最大值所在位置,当前区间有几个数字没有被删除。
然后从前往后开始,设当前位置为P,剩K步可以拿,那么就要从P后面拿一个a[Q],他要比a[P]大且区间[P,Q]未删除数字个数不超过K+1。可以先找到距离P最远X的且[P,X]里面未删除的数字不超过K+1个。然后查询[P,X]中最大值,如果比a[P]大就可以换了,不然的话就P++;Q放到前面之后就在线段树里面进行删除。
距离P最远X的且[P,X]里面未删除的数字不超过K+1个,这一步可以通过找[0,X]不超过某个数字来做,可以有logn.
以上步骤均可以在logn以内实现,所以总复杂度是nlogn。#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<string.h>#include<algorithm>using namespace std;#define ls rt<<1#define rs rt<<1|1const int maxn=200000;//删除//找区间最大值//可以分成两步写呀//线段树的话果然还是想好操作然后再想维护什么比较容易有思路struct node{ int l,r; int ma; int num;}T[maxn<<2];int times;int here[maxn];int flag[maxn];int a[maxn],a2[maxn];void pushup(int rt){ T[rt].num=T[ls].num+T[rs].num; T[rt].ma=max(T[ls].ma,T[rs].ma);}void build(int rt,int L,int R){ T[rt].l=L;T[rt].r=R; if(L==R) { T[rt].ma=a[L]; T[rt].num=1; return; } int mid=(L+R)>>1; build(ls,L,mid); build(rs,mid+1,R); pushup(rt);}//查询最大值并删除void update(int rt,int pos)//标记为0{// cout<<T[rt].l<<" "<<T[rt].r<<" "<<T[rt].ma<<endl; if(T[rt].l==T[rt].r) { T[rt].num=0; T[rt].ma=0; flag[T[rt].l]=1; return; } int mid=(T[rt].l+T[rt].r)>>1; if(pos<=mid) update(ls,pos); else update(rs,pos); pushup(rt);}int query(int rt,int L,int R){// cout<<rt<<" "<<L<<" "<<R<<endl; if(T[rt].l>=L && T[rt].r<=R) { return T[rt].ma; } int mid=(T[rt].l+T[rt].r)>>1; int tmp=0; if(L<=mid) tmp=max(tmp,query(ls,L,R)); if(R>mid) tmp=max(tmp,query(rs,L,R));//多个地方的 L R的处理 return tmp;}int getpos(int rt,int pos){// cout<<T[rt].l<<" "<<T[rt].r<<" "<<T[rt].num<<endl; if(T[rt].l==T[rt].r) { return T[rt].l; } if(T[ls].num>=pos) return getpos(ls,pos); else return getpos(rs,pos-T[ls].num);}int gettimes(int rt,int L,int R){// cout<<L<<" "<<R<<" "<<T[rt].num<<endl; if(T[rt].l>=L && T[rt].r<=R) { return T[rt].num; } int mid=(T[rt].l+T[rt].r)>>1; int tmp=0; if(L<=mid) tmp+=gettimes(ls,L,R); if(R>mid) tmp+=gettimes(rs,L,R); return tmp;}int main(){ int n,k;// freopen("in.txt","r",stdin); while(scanf("%d%d",&n,&k)!=EOF) { for(int i=1;i<=n;i++) scanf("%d",&a[i]), here[a[i]]=i; memset(flag,0,sizeof(flag)); build(1,1,n); int p=0; for(int i=1;i<=n;i++) { if(k==0) break; p++; int pos=getpos(1,k+1); int tmp=query(1,1,pos);// cout<<"pos "<< pos<<" ma"<<tmp<<endl; update(1,here[tmp]); k-=gettimes(1,1,here[tmp]); a2[p]=tmp;// cout<<k<<" k"<<endl; }// cout<<k<<endl; for(int i=1;i<=n;i++) { if(flag[i]==0) a2[++p]=a[i]; }// cout<<"--------------------------------"<<endl; for(int i=1;i<=n;i++) cout<<a2[i]<<endl; } return 0;}
0 0
- 51nod 1364 线段树
- 51nod 1631 线段树
- 51nod 1672 线段树
- 51nod-1364 最大字典序排列(线段树)
- 51nod 1463:找朋友 线段树
- 51NOD 1672 区间交 线段树
- 51nod 1287 加农炮 【线段树】
- 51Nod 1287 加农炮 ( 暴力/线段树
- 51nod 1562 玻璃切割 【线段树】
- 51Nod 1487 思维+线段树
- 51nod 1461 稳定桌[线段树]
- 51nod 1781 Pinball【DP】【线段树】
- 51Nod-线段相交
- 51nod 1264 线段相交
- 51NOD 1264 线段相交
- 51 nod 1264 线段相交
- [51nod]1264 线段相交
- 51nod 1264 线段相交
- android meta-data的使用以及含义
- DCOS之k8s的容器监测探针
- 预处理指令,以“#”开头
- UVA NO.624 CD(打印路径,简单背包问题)
- 数组中各方法
- 51nod 1364 线段树
- Java模式设计原则
- 使用PictureBox显示分割图像
- Xcode找不到对应的dylib库
- 变量声明提前和函数声明提前
- Android Activity 的四种启动模式 lunchMode 和 Intent.setFlags();
- 004_Http之response响应头-03禁用浏览器缓存
- 机器学习 知识图谱
- C# 中的委托和事件