POJ2104 整体二分、树套树

题目大意:
给你一串数字,然后给你两种操作:
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)

#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)构造答案+树状数组单点查询+平衡树查询
#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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值