博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
洛谷P2617 Dynamic Rankings (主席树)
阅读量:5288 次
发布时间:2019-06-14

本文共 3424 字,大约阅读时间需要 11 分钟。

洛谷P2617 Dynamic Rankings

题目描述

给定一个含有n个数的序列a[1],a[2],a[3]……a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i+2]……a[j]中第k小的数是多少(1≤k≤j-i+1),并且,你可以改变一些a[i]的值,改变后,程序还能针对改变后的a继续回答上面的问题。你需要编一个这样的程序,从输入文件中读入序列a,然后读入一系列的指令,包括询问指令和修改指令。

对于每一个询问指令,你必须输出正确的回答。

输入输出格式

输入格式:

第一行有两个正整数n(1≤n≤10000),m(1≤m≤10000)。分别表示序列的长度和指令的个数。

第二行有n个数,表示a[1],a[2]……a[n],这些数都小于10^9。接下来的m行描述每条指令,每行的格式是下面两种格式中的一种。 Q i j k 或者 C i t

  • Q i j k (i,j,k是数字,1≤i≤j≤n, 1≤k≤j-i+1)表示询问指令,询问a[i],a[i+1]……a[j]中第k小的数。

  • C i t (1≤i≤n,0≤t≤10^9)表示把a[i]改变成为t。

输出格式:

对于每一次询问,你都需要输出他的答案,每一个输出占单独的一行。

输入输出样例

输入样例#1:

5 3

3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3

输出样例#1:

3

6

说明

10%的数据中,m,n≤100;

20%的数据中,m,n≤1000;

50%的数据中,m,n≤10000。

对于所有数据,m,n≤100000

请注意常数优化,但写法正常的整体二分和树套树都可以以大约1000ms每个点的时间过。

来源:bzoj1901

本题数据为洛谷自造数据,使用耗时5分钟完成数据制作。

Solution

带修改主席树...

我们都知道静态主席树每颗节点保存当前管理区间的所有权值的出现次数,因为每棵树的形态结构都相同,所以我们可以通过对树进行相加减求得静态区间第k大(小)

但是如果要修改的话,若我们暴力修改的话是需要对前缀和进行\(O(nlogn)\)的单次修改,会T飞

那么我们就需要请擅长管理前缀和的树状数组来处理,而主席树现在只负责维护前缀和的辅助数组,套上树状数组的话,我们就只需要修改\(logn\)的节点,复杂度\(O(log^2n)\)

注意1:与静态主席树不同的是带修改主席树中的每一棵线段树所管理的信息都是独立的,不需要继承上一棵线段树的信息

注意2:不但需要对原数组进行离散,还要把修改之后的值也加进来,不然修改后的值不一定能在离散数组中找得到位置

Code

#include
#define Min(a,b) ((a)<(b)?(a):(b))#define Max(a,b) ((a)>(b)?(a):(b))#define rg register#define mid ((l+r)>>1)#define in(i) (i=read())using namespace std;const int N=1e5+10;int read() { int ans=0,f=1; char i=getchar(); while(i<'0' || i>'9') {if(i=='-') f=-1; i=getchar();} while(i>='0' && i<='9') ans=(ans<<1)+(ans<<3)+i-'0',i=getchar(); return ans*=f;}int n,m,num,tot,cnt1,cnt2;int a[N],h[N<<1],now[5][N<<8],rt[N<<8];struct Tree { int v,l,r;}t[N<<8];struct Query{ int op,l,r,k;}q[N];int lowbit(int x) {return x&-x;}void update(int &u,int l,int r,int pos,int val) {//不需要继承上一棵线段树的信息,带修改主席树的每一棵线段树管理的信息都是独立的 if(!u) u=++tot; t[u].v+=val; if(l==r) return; if(pos<=mid) update(t[u].l,l,mid,pos,val); else update(t[u].r,mid+1,r,pos,val);}void modify(int x,int val) {//处理出哪些节点需要修改 int k=lower_bound(h+1,h+1+num,a[x])-h; for (int i=x;i<=n;i+=lowbit(i)) update(rt[i],1,num,k,val);}int query(int l,int r,int k,int sum=0) { if(l==r) return h[l]; for (int i=1;i<=cnt1;i++) sum-=t[t[now[1][i]].l].v; for (int i=1;i<=cnt2;i++) sum+=t[t[now[2][i]].l].v; if(k<=sum) { for (int i=1;i<=cnt1;i++) now[1][i]=t[now[1][i]].l;//如果当前左子树的前缀次数小于等于k,那么答案就在左子树中 for (int i=1;i<=cnt2;i++) now[2][i]=t[now[2][i]].l; return query(l,mid,k); } else {//否则在右子树中 for (int i=1;i<=cnt1;i++) now[1][i]=t[now[1][i]].r; for (int i=1;i<=cnt2;i++) now[2][i]=t[now[2][i]].r; return query(mid+1,r,k-sum); }}int pre_query(int l,int r,int k) {//处理出哪些节点需要查询 cnt1=cnt2=0, l--; for (int i=l;i;i-=lowbit(i)) now[1][++cnt1]=rt[i]; for (int i=r;i;i-=lowbit(i)) now[2][++cnt2]=rt[i]; return query(1,num,k);}int main(){ in(n), in(m); for (int i=1;i<=n;i++) in(a[i]),h[++num]=a[i]; for (int i=1;i<=m;i++) { char s[10]; scanf("%s",s); if(s[0]=='Q') in(q[i].l), in(q[i].r), in(q[i].k), q[i].op=1; else in(q[i].l), in(q[i].r), h[++num]=q[i].r, q[i].op=0; } sort(h+1,h+1+num), num=unique(h+1,h+1+num)-h-1; for (int i=1;i<=n;i++) modify(i,1); for (int i=1;i<=m;i++) { if(q[i].op==0) { modify(q[i].l,-1); a[q[i].l]=q[i].r; modify(q[i].l,1); } else printf("%d\n",pre_query(q[i].l,q[i].r,q[i].k)); }}

转载于:https://www.cnblogs.com/real-l/p/9879366.html

你可能感兴趣的文章
.NET Framework框架介绍
查看>>
Git学习——Git分支篇(未完)
查看>>
MySql 修改中文乱码/ 表名不区分大小写
查看>>
C#代码怎样在Windows窗体中显示从数据库读出的图片
查看>>
effective c++ 7: Declare destructors virtual in polymorphic base classes
查看>>
ActionBar
查看>>
Ajax上传文件到C#Action中
查看>>
实现android上解析Json格式数据功能
查看>>
最短路算法--模板
查看>>
利用树莓派3搭建可以发射无线局域网的微型服务器
查看>>
Linux查看系统的基本信息
查看>>
eclipse jsp 文字设置
查看>>
Android--多线程之AsyncTask
查看>>
cxdbImage以及图像显示
查看>>
36、UI contrast and settings
查看>>
HDU 2070 Fibbonacci Number
查看>>
骰子作业
查看>>
C++虚函数与纯虚函数用法与区别(转)
查看>>
jq中的substring(x)和substring(x,y) 字符截取用法
查看>>
BNUOJ-22868-Movie collection(树状数组)
查看>>