caioj1442:第k小的数Ⅱ
Posted Star_Feel
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了caioj1442:第k小的数Ⅱ相关的知识,希望对你有一定的参考价值。
【传送门:caioj1442】
简要题意:
给出n个点,每个点都有一个权值,m个操作,操作有两种:第一种是询问l到r的第k小的值,然后输出这个值,第二种是将第x个点的值改为k
题解:
又是一道主席树的例题,不过简直比前两题(caioj1441,caioj1443)难不止一点点
看到第一种操作,我们可以用主席树来搞,但是第二种操作的话,我们就要用树状数组来维护每个第二种操作所带来的影响,而且每次要求第一种操作的时候都要先在树状数组中找到相应的点,然后再处理主席树
参考代码:
#include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> #include<cmath> using namespace std; int a[210000]; struct node { int lc,rc,c; }tr[3100000];int cnt; int rt[210000];int n; int ust[210000];//树状数组 int lowbit(int x)//树状数组用来找上司或下属 { return x&-x; } void Link(int &u,int l,int r,int p,int c) { if(u==0) u=++cnt; tr[u].c+=c; if(l==r) return ; int mid=(l+r)/2; if(p<=mid) Link(tr[u].lc,l,mid,p,c); else Link(tr[u].rc,mid+1,r,p,c); } void Merge(int &u1,int u2) { if(u1==0){u1=u2;return ;} if(u2==0) return ; tr[u1].c+=tr[u2].c; Merge(tr[u1].lc,tr[u2].lc); Merge(tr[u1].rc,tr[u2].rc); } void Turn(int u,int c)//树状数组的实时更新 //c==-1时树状数组记录每个点所在线段树的根 //c==0时记录左孩子 //c==1时记录右孩子 { while(u>=n+1) { if(c==-1) ust[u]=rt[u]; else if(c==0) ust[u]=tr[ust[u]].lc; else if(c==1) ust[u]=tr[ust[u]].rc; u-=lowbit(u); } } void Modefy(int u,int p,int c)//对于修改值就用树状数组来节约时间 { while(u<=2*n) { Link(rt[u],0,1000000000,p,c); u+=lowbit(u); } } int Getsum(int u)//求出修改过后树状数组得到的值 { int ret=0; while(u>=n+1) { ret+=tr[tr[ust[u]].lc].c; u-=lowbit(u); } return ret; } int Ask(int u1,int u2,int p1,int p2,int l,int r,int p) { if(l==r) return l; int c=tr[tr[u2].lc].c-tr[tr[u1].lc].c+Getsum(p2+n)-Getsum(p1+n); int mid=(l+r)/2; if(p<=c) { Turn(p1+n,0); Turn(p2+n,0); return Ask(tr[u1].lc,tr[u2].lc,p1,p2,l,mid,p); } else { Turn(p1+n,1); Turn(p2+n,1); return Ask(tr[u1].rc,tr[u2].rc,p1,p2,mid+1,r,p-c); } } int main() { int m; scanf("%d%d",&n,&m); cnt=0;memset(rt,0,sizeof(rt)); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); Link(rt[i],0,1000000000,a[i],1); Merge(rt[i],rt[i-1]); } char st[2]; for(int i=1;i<=m;i++) { scanf("%s",st+1); if(st[1]==\'Q\') { int l,r,k; scanf("%d%d%d",&l,&r,&k); Turn(l+n-1,-1); Turn(r+n,-1); printf("%d\\n",Ask(rt[l-1],rt[r],l-1,r,0,1000000000,k)); } else { int p,c; scanf("%d%d",&p,&c); Modefy(p+n,a[p],-1); a[p]=c; Modefy(p+n,a[p],1); } } return 0; }
以上是关于caioj1442:第k小的数Ⅱ的主要内容,如果未能解决你的问题,请参考以下文章
快排划分思想的应用-求第k大数或者第k小的数(求前k大数或者前k小的数)