hdu 5412 CRB and Queries(动态区间第k大值,区间能修改)(整体二分,树状数组套平衡树,线段树套treap)

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=5412

解题思路:

官方题解:

In this problem, we can use BIT and Treap.

We use BIT for coding skill and each element of BIT is Treap.

First, for each coding skill, whose index can be added for BIT.

So each index can be inserted corresponding Treap.

The update operation can be done by deleting current index from old value’s structure and adding this to the new value.

The query operation can be done by deciding left bit from its binary representation.

This is possible by using BIT.

The number of corresponding skills can be counted by using Treap.

We count the index from l to rr in Treap.

Time complexity:O(N\cdot log^{2}N)O(Nlog2N)


题目大意:

给你一串数字,然后给你两种操作:

1:1 l v  

操作一:把下标为l的点的值替换为v

2:2 l r k  

操作二:在[l,r]区间求第k大值!


1.整体二分

对于初始数字,变为插入操作

按操作的时间顺序排列各个操作,对于修改操作拆为删除和加入操作:

1 删除之前插入的数字,2. 加入新的数字

接下来分治二分答案:

对于mid,如果插入或者删除的数字<=mid那么应该放到左区间,并且用树状数组(其他数据结构也行)

维护前X个位置有多少个数字在左边。

对于询问:如果l,r区间在左边的数字>=k那么答案在左边,否则答案在右边,并且更新K,k=k-这个区间去左边的数字个数

当low =high的时候,说明low就是答案了,只要更新询问还在low,low之间的答案即可。

复杂度分析:分治的深度是log(S)s是数据的范围。

每个询问每次被分到左边或者右边,会有log(s)次操作。每个插入或者删除也是。

但是要用树状数组维护多少个数字在左边,要log(n)次更新或者查询。

复杂度是n*log(s)*log(n)

AC代码:

#include<iostream>  
#include<cstring>  
#include<algorithm>  
#include<cstdio>  
#include<vector>  
using namespace std;  
#define maxn 300007  
int tree[maxn];  
void add(int p,int n){  
    for(;p<maxn;p+=p&(-p))  
        tree[p]+=n;  
}  
int query(int p){  
    int ans = 0;  
    for(;p>0;p-=p&(-p))  
        ans += tree[p];  
    return ans;  
}  
  
struct Node{  
    int l,r,k,ty,ans;  
};  
Node p[maxn];  
int id1[maxn],id2[maxn];  
void CDQ(int L,int R,int low,int high){  
    if(R < L) return ;  
    if(low == high ){  
        for(;L<=R;L++){  
            p[id1[L]].ans = low;  
        }  
        return ;  
    }  
    int mid = (low+high)/2,l=L,r=R,k,u;  
    for(int i = L;i <= R; i++){  
        u = id1[i];  
       if(p[u].ty == 2){  
            k = query(p[u].r) - query(p[u].l-1);  
            if(k >= p[u].k) id2[l++] = u;  
            else {  
                p[u].k -= k;  
                id2[r--] = u;  
            }  
        }  
        else if(p[u].k <= mid){  
            add(p[u].l,p[u].ty);  
            id2[l++] = u;  
        }  
        else id2[r--] =  u;  
    }  
  
    for(int i = L; i <= R; i++){  
        u = id1[i];  
        if(p[u].ty != 2 && p[u].k <= mid) add(p[u].l,-p[u].ty);  
    }  
    for(k=L;k<l;k++)  
        id1[k] = id2[k];  
    for(r=R;k<=R;k++)  
        id1[k] = id2[r--];  
    CDQ(L,l-1,low,mid);  
    CDQ(l,R,mid+1,high);  
}  
  
int num[maxn];  
  
int main(){  
    int n,q,t,cnt;  
    memset(tree,0,sizeof(tree));  
    while(scanf("%d",&n)!=EOF){  
        for(cnt=0;cnt<n;cnt++){  
            scanf("%d",&p[cnt].k);  
            p[cnt].ty = 1;  
            p[cnt].l = cnt+1;  
            num[cnt+1] = p[cnt].k;  
        }  
        scanf("%d",&q);  
        int ty,l,v;  
        for(int i = 0;i < q; i++,cnt++){  
            scanf("%d",&p[cnt].ty);  
            if(p[cnt].ty == 1){  
                scanf("%d%d",&l,&v);  
                p[cnt].ty = -1;  
                p[cnt].k = num[l];  
                p[cnt].l = l;  
                cnt++;  
                num[l] = v;  
                p[cnt].ty = 1;  
                p[cnt].k = v;  
                p[cnt].l = l;  
            }  
            else {  
                scanf("%d%d%d",&p[cnt].l,&p[cnt].r,&p[cnt].k);  
            }  
        }  
        for(int i = 0;i < cnt; i++)  
            id1[i] = i;  
        CDQ(0,cnt-1,0,1000000000);  
        for(int i = 0;i < cnt; i++){  
            if(p[i].ty == 2) printf("%d\n",p[i].ans);  
        }  
    }  
    return 0;  
}  



2.树状数组套平衡树

我们将树状数组的每一个节点代表对应的数字(需要将询问读入,然后把所有出现的数字离散化),平衡树中保存每一个数在序列中的下标。
修改:将原来序列中的数字在对应的树状数组套的平衡树中删除,再同理插入新的数字
询问:考虑答案的二进制表示,通过巧妙地运用树状数组的性质,我们可以从高位往地位贪心的构造答案,每次贪心在平衡树中查找对应区间的数字个数。
修改时间复杂度O(log^2N)
询问时间复杂度O(log^2N)构造答案+树状数组单点查询+平衡树查询

AC代码:

#include <ext/pb_ds/tree_policy.hpp>  
#include <ext/pb_ds/assoc_container.hpp>  
#include <cstdio>  
#include <cstring>  
#include <algorithm>  
#include <cstdlib>  
#define MAXN 100002  
using namespace std;  
using namespace __gnu_pbds;  
struct query{int t,l,r,k;}q[MAXN];  
//保存询问   
tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update>bit[MAXN*2];  
tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update>::iterator it;  
//exSTL红黑树,G++ Only,低版本中null_type为null_mapped_type  
int num[MAXN],n,ql,qr,k,oper,lsh[MAXN*2],H,ct;  
//lsh为离散化数组 ,H为贪心构造答案的最高位   
int lowbit(int x){return x&(-x);}  
void add(int p,int num){  
    for(int i=p;i<=ct;i+=lowbit(i))  
        bit[i].insert(num);  
}  
//树状数组更新   
void del(int p,int num){  
    for(int i=p;i<=ct;i+=lowbit(i))  
        bit[i].erase(num);  
}  
//树状数组删除   
int pos(int num){return lower_bound(lsh,lsh+ct,num)-lsh+1;}  
// 返回离散化下标   
int query(int l,int r,int k){  
    int num=0;  
    for(int i=H;i;i>>=1){ //高位往地位贪心   
        int tmp=num+i;  
        if(tmp>ct)continue;  
        int kth=bit[tmp].order_of_key(r+1)-bit[tmp].order_of_key(l);  
        //平衡树查询   
        if(kth>=k)continue;  
        num=tmp,k-=kth;  
    }  
    return num;  
}  
//贪心构造答案   
int main(){  
    int t;  
    while(scanf("%d",&n)!=EOF){  
        ct=0;  
        for(int i=1;i<=n;i++){  
            scanf("%d",&num[i]);  
            lsh[ct++]=num[i];  
        }  
        scanf("%d",&t);  
        for(int i=0;i<t;i++){  
            scanf("%d",&q[i].t);  
            if(q[i].t==1){  
                scanf("%d%d",&q[i].l,&q[i].k);  
                lsh[ct++]=q[i].k;  
            }else scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].k);  
        }  
        sort(lsh,lsh+ct);  
        ct=unique(lsh,lsh+ct)-lsh;  
        for(int i=1;i<=ct;i++)bit[i].clear();  
        H=1;  
        while(H*2<ct)H*=2;  
        for(int i=0;i<t;i++)if(q[i].t==1)q[i].k=pos(q[i].k);  
        for(int i=1;i<=n;i++){  
            num[i]=pos(num[i]);  
            add(num[i],i);  
        }  
        for(int i=0;i<t;i++){  
            if(q[i].t==1){  
                del(num[q[i].l],q[i].l);  
                add(q[i].k,q[i].l);  
                num[q[i].l]=q[i].k;  
            }else printf("%d\n",lsh[query(q[i].l,q[i].r,q[i].k)]);      
        }  
    }  
    return 0;  
}  



3.线段树套treap

//#pragma warning (disable:4786)  
//#pragma comment(linker,"/STACK:102400000,102400000")  //手动扩栈  
//#include <bits/stdc++.h>  
#include <cstdio>  
#include <cmath>  
#include <cstring>  
#include <string>  
#include <cstdlib>  
#include <climits>  
#include <ctype.h>  
#include <queue>  
#include <stack>  
#include <vector>  
#include <utility>  
#include <deque>  
#include <set>  
#include <map>  
#include <iostream>  
#include <algorithm>  
using namespace std;  
const double eps = 1e-9;  
const double PI = acos(-1.00);  
//#define PI 3.1415926535897932384626433832795  
const double e = exp(1.0);  
#define INF 0x3f3f3f3f  
//#define INF 1e18  
//typedef long long LL;  
//typedef __int64 LL;  
#define ONLINE_JUDGE  
#ifndef ONLINE_JUDGE  
freopen("in.txt", "r", stdin);  
freopen("out.txt", "w", stdout);  
#endif  
  
  
#define N 600010  
#define M 100010  
struct treap  
{  
    int key,wht,count,sz,ch[2];  
} tp[N*15];  
int tree[N<<1];  
int nodecount,root;  
int IDX(int l,int r)  
{  
    return l+r | l!=r;  
}  
void init()  
{  
    tp[0].sz=0;  
    tp[0].wht=-INF;  
    nodecount=0;  
    root=0;  
}  
void update(int x)  
{  
    tp[x].sz=tp[tp[x].ch[0]].sz+tp[x].count+tp[tp[x].ch[1]].sz;  
}  
void rotate(int &x,int t)  
{  
    int y=tp[x].ch[t];  
    tp[x].ch[t]=tp[y].ch[!t];  
    tp[y].ch[!t]=x;  
    update(x);  
    update(y);  
    x=y;  
}  
void insert(int &x,int t)  
{  
    if(! x)  
    {  
        x=++nodecount;  
        tp[x].key=t;  
        tp[x].wht=rand();  
        tp[x].count=1;  
        tp[x].ch[0]=tp[x].ch[1]=0;  
    }  
    else if(tp[x].key==t)  tp[x].count++;  
    else  
    {  
        int k=tp[x].key<t;  
        insert(tp[x].ch[k],t);  
        if(tp[x].wht<tp[tp[x].ch[k]].wht) rotate(x,k);  
    }  
    update(x);  
}  
void erase(int &x,int t)  
{  
    if(tp[x].key==t)  
    {  
        if(tp[x].count==1)  
        {  
            if(! tp[x].ch[0] && ! tp[x].ch[1])  
            {  
                x=0;  
                return;  
            }  
            rotate(x,tp[tp[x].ch[0]].wht<tp[tp[x].ch[1]].wht);  
            erase(x,t);  
        }  
        else tp[x].count--;  
    }  
    else erase(tp[x].ch[tp[x].key<t],t);  
    update(x);  
}  
int select(int x,int t)  
{  
    if(! x) return 0;  
    if(tp[x].key>t) return select(tp[x].ch[0],t);  
    return tp[x].count+tp[tp[x].ch[0]].sz+select(tp[x].ch[1],t);  
}  
int a[N],b[N],ord[M][5],lb;  
int n,m,tt;  
int search(int x)  
{  
    int l=1,r=b[0],mid;  
    while (l<=r)  
    {  
        mid=(l+r)>>1;  
        if(b[mid]==x) return mid;  
        if(b[mid]<x) l=mid+1;  
        else r=mid-1;  
    }  
}  
void treeinsert(int l,int r,int i,int x)  
{  
    insert(tree[IDX(l,r)],x);  
    if(l==r) return;  
    int m=(l+r)>>1;  
    if(i<=m) treeinsert(l,m,i,x);  
    else treeinsert(m+1,r,i,x);  
}  
void treedel(int l,int r,int i,int x)  
{  
    erase(tree[IDX(l,r)],x);  
    if(l==r) return;  
    int m=(l+r)>>1;  
    if(i<=m) treedel(l,m,i,x);  
    else treedel(m+1,r,i,x);  
}  
int query(int l,int r,int x,int y,int k)  
{  
    if(l==r) return l;  
    int m=(l+r)>>1;  
    int ans=select(tree[IDX(l,m)],y)-select(tree[IDX(l,m)],x);  
    if(ans>=k) return query(l,m,x,y,k);  
    return query(m+1,r,x,y,k-ans);  
}  
int main ()  
{  
    while (~scanf("%d",&n))  
    {  
        b[0]=1;  
        lb=0;  
        memset(tree,0,sizeof(tree));  
        init();  
        for(int i=1; i<=n; i++)  
        {  
            scanf("%d",&a[i]);  
            b[++lb]=a[i];  
        }  
        scanf("%d",&m);  
        for(int i=1; i<=m; i++)  
        {  
            int op;  
            int x,y,c;  
            scanf("%d",&op);  
            if(op == 2)  
            {  
                scanf("%d %d %d",&x,&y,&c);  
                ord[i][1]=1;  
                ord[i][2]=x;  
                ord[i][3]=y;  
                ord[i][4]=c;  
            }  
            else  
            {  
                scanf("%d %d",&x,&y);  
                ord[i][1]=2;  
                ord[i][2]=x;  
                ord[i][3]=y;  
                b[++lb]=y;  
            }  
        }  
        sort(b+1,b+1+lb);  
        for(int i=1; i<=lb; i++)  
            if(b[i]!=b[b[0]]) b[++b[0]]=b[i];  
        for(int i=1; i<=n; i++)  
        {  
            a[i]=search(a[i]);  
            treeinsert(1,b[0],a[i],i);  
        }  
        for(int i=1; i<=m; i++)  
        {  
            if(ord[i][1]==1)  
                printf("%d\n",b[query(1,b[0],ord[i][2]-1,ord[i][3],ord[i][4])]);  
            else  
            {  
                treedel(1,b[0],a[ord[i][2]],ord[i][2]);  
                a[ord[i][2]]=search(ord[i][3]);  
                treeinsert(1,b[0],a[ord[i][2]],ord[i][2]);  
            }  
        }  
    }  
    return 0;  
}  


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值