HDU HDOJ5412(树套树or整体二分

题目:要求支持带修改维护区间第k大的值。所谓的动态区间第k大。

思路:题解说的是树状数组套treap,然而没想通树状数组怎么维护。。。线段树的话就是把所有的值离散化一下,离线建个关于值的线段树,每个节点是一个treap,treap里的值用位置做关键字,然后做区间查询,复杂度是O(nlogn*logn)。基本也是经典的树套树做法。。。。然后赛后写了两遍都没过。。。。。今天心血来潮再挑战一下,结果从8点调到晚上1点。其间各种爆内存各种re各种t。。。。。随机数据对拍了好久没拍出问题来。。。。然后一直t,,后来突然想到改交vc,结果wa了。。。。然后搞了个数据,结果本地直接re。。简直凌乱了。。。然后随便试试把数组开大。。。结果就过了。。我草。。。明明g++开4e6都爆内存,vc开5e6居然还有富余。。。。然后再开个栈外挂,速度直逼标程。。。。

结论:这题有毒。。。。基本都是整体二分过的。。。有时间学习一下。。免得这么但疼。。。

/*
* @author:  Cwind
* http://www.cnblogs.com/Cw-trip/
*/
#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
using namespace std;
#define IOS std::ios::sync_with_stdio(false);std::cin.tie(0)
#define pb push_back
#define PB pop_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps 0.00000001
#define IINF (1<<29)
#define LINF (1ll<<59)
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> P;
const int maxNode=5e6+300;
struct treapNode{
    treapNode *ch[2];
    int right,val;
    int sz;
    treapNode():sz(0){}
    int cmp(int x) const {
        if(x==val) return -1;
        return x>val;
    }
    void maintain(){
        sz=ch[0]->sz+ch[1]->sz+1;
    }
}pool[maxNode];
int ph=0;
treapNode *null=new treapNode();
treapNode *newtreapNode(int v){
    treapNode *n=&pool[ph++];
    n->val=v;
    n->right=rand();
    n->ch[0]=n->ch[1]=null;
    return n;
}
void rotate(treapNode *&o,int d){
    treapNode *k=o->ch[d^1];
    o->ch[d^1]=k->ch[d];
    k->ch[d]=o;
    o->maintain();
    k->maintain();
    o=k;
}
void insert(treapNode *&o,int x){
    if(o==null) o=newtreapNode(x);
    else{
        int d=o->val<x;
        insert(o->ch[d],x);
        if(o->ch[d]->right>o->right) rotate(o,d^1);
    }
    o->maintain();
}
void remove(treapNode *&o,int x){
    int d=o->cmp(x);
    if(d==-1){
        if(o->ch[0]==null) o=o->ch[1];
        else if(o->ch[1]==null) o=o->ch[0];
        else{
            int d2=o->ch[0]->right>o->ch[1]->right;
            rotate(o,d2);
            remove(o->ch[d2],x);
        }
    }else remove(o->ch[d],x);
    if(o!=null) o->maintain();
}
int findless(treapNode *o,int x){
    int r=0;
    while(o!=null){
        if(o->val>x){
            o=o->ch[0];
        }
        else{
            r+=o->ch[0]->sz+1;
            o=o->ch[1];
        }
    }
    return r;
}
int culbetween(treapNode *o,int l,int r){
    return findless(o,r)-findless(o,l-1);
}
int ah;
const int maxn=1e6+3000;
struct QU{
    int t,a,b,c;
}qu[maxn];
struct segNode{
    int l,r;
    treapNode *root;
    segNode *ch[2];
}segpool[maxNode];
int segh=0;
segNode *newsegNode(){
    segNode *n=&segpool[segh++];
    n->root=null;
    return n;
}
void buildSeg(segNode *n,int l,int r){
    n->l=l,n->r=r;
    if(r-l<=1) return;
    n->ch[0]=newsegNode();
    n->ch[1]=newsegNode();
    buildSeg(n->ch[0],l,(l+r)/2);
    buildSeg(n->ch[1],(r+l)/2,r);
}
void segInsert(segNode *n,int x,int p){
    int r=n->r,l=n->l;
    int mid=(r+l)/2;
    treapNode *&tn=n->root;
    insert(tn,p);
    if(r-l<=1) return;
    if(x>=mid)
        segInsert(n->ch[1],x,p);
    else
        segInsert(n->ch[0],x,p);
}
void segRemove(segNode *n,int x,int p){
    int r=n->r,l=n->l;
    int mid=(r+l)/2;
    treapNode *&tn=n->root;
    remove(tn,p);
    if(r-l<=1) return;
    if(x>=mid)
        segRemove(n->ch[1],x,p);
    else
        segRemove(n->ch[0],x,p);
}
int segQuery(segNode *n,int a,int b,int k){
    treapNode *tn=n->root;
    segNode *chl=n->ch[0],*chr=n->ch[1];
    int l=n->l,r=n->r;
    if(r-l<=1) return l;
    int q1=culbetween(chl->root,a,b);
    if(q1>=k) return segQuery(chl,a,b,k);
    else return segQuery(chr,a,b,k-q1);
}
int N;
int deca[maxn];
int idx(int x){
    return lower_bound(deca,deca+ah,x)-deca;
}
segNode *segRoot;
void clear(){
    ph=0;
    segh=0;
    segRoot=newsegNode();
}
int a[maxn];

template <class T>
inline bool scan_d(T &ret) {
    char c;
    int sgn;
    if(c=getchar(),c==EOF) return 0; //EOF
    while(c!='-'&&(c<'0'||c>'9')) c=getchar();
    sgn=(c=='-')?-1:1;
    ret=(c=='-')?0:(c-'0');
    while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
    ret*=sgn;
    return 1;
}
inline void out(int x) {
    if(x>9) out(x/10);
    putchar(x%10+'0');
}

int main(){
    freopen("/home/slyfc/CppFiles/in","r",stdin);
    freopen("/home/slyfc/CppFiles/out","w",stdout);
    while(cin>>N){
        clear();
        for(int i=1;i<=N;i++){ 
            scan_d(a[i]);
            deca[i-1]=a[i];
        }
        int Q;
        scan_d(Q);
        int an=N;
        for(int i=1;i<=Q;i++){
            int t;
            scan_d(t);
            if(t==1){
                int l,v;
                scan_d(l);scan_d(v);
                qu[i].a=l,qu[i].b=v;
                deca[an++]=v;
            }
            else{
                int l,r,k;
                scan_d(l);scan_d(r);scan_d(k);
                qu[i].a=l,qu[i].b=r,qu[i].c=k;
            }
            qu[i].t=t;
        }
        sort(deca,deca+an);
        ah=unique(deca,deca+an)-deca;
        buildSeg(segRoot,0,ah);
        for(int i=1;i<=N;i++)
            segInsert(segRoot,idx(a[i]),i);
        for(int i=1;i<=Q;i++){
            if(qu[i].t==1){
                int l=qu[i].a,v=qu[i].b;
                segRemove(segRoot,idx(a[l]),l);
                a[l]=v;
                segInsert(segRoot,idx(v),l);
            }else{
                int l=qu[i].a,r=qu[i].b,k=qu[i].c;
                printf("%d\n",deca[segQuery(segRoot,l,r,k)]);
            }
        }
    }
    return 0;
}
View Code

 整体二分解法:

(弱渣如我看了几个小时才看懂= =).总的来说,整体二分就是二分结果,也就是说把最后答案在一个区间内的询问一块处理.但是看完这个思路之后我想了很久,感觉操作顺序和值之间有着无法逾越的鸿沟.............然后下午开始看代码......在这里详细叙述一下我的理解:

首先肯定是离散话,然后才能按值二分答案,这个过程中,值和操作是一并处理的.比如现在做到了l,r这个值的区间,此时所有的询问的答案都在这个区间里,更新操作的值也在这个范围里,此时,在当前区间里用树状数组统计区间数目,这个算法能够加速的一个主要原理是它同时处理值小于mid的所有询问,这样就兼顾了二分的值和询问的数目.把修改操作改成两个操作,增加删除,可以发现是没有影响的,对于当前统计,只有小于mid的值的更新才对结果有影响,把这些值按顺序进行处理,就可以计算出到达某个询问时小于mid的数有多少(区间用树状数组统计).

这个算法不论是实现还是速度上都相当有优势.

/*
* @author:  Cwind
*/
#include <bits/stdc++.h>

using namespace std;
#define IOS std::ios::sync_with_stdio (false);std::cin.tie(0)
#define pb push_back
#define PB pop_back
#define bk back()
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps (1e-10)
#define INF (1000000300)
#define clr(x) memset((x),0,sizeof (x))
#define cp(a,b) memcpy((a),(b),sizeof (b))

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,string> P;

const int maxn=3e5+300;
int nq,nv;
int a[maxn];
int dval[maxn];
int ans[maxn];
struct Node{
    int op,l,r,k;
    int id;
    Node(int op,int l,int r,int k,int id):op(op),l(l),r(r),k(k),id(id){}
    Node(){}
}q[maxn],q1[maxn],q2[maxn];
struct BIT{
    int a[maxn];
    void add(int p,int v){
        while(p<maxn) a[p]+=v,p+=p&-p;
    }
    int sum(int p){
        int ans=0;
        while(p) ans+=a[p],p-=p&-p;
        return ans;
    }
    void clear(){clr(a);}
}B;
void solve(int h,int t,int l,int r){
    int nq1=0,nq2=0;
    if(r-l==1){
        for(int i=h;i<t;i++)
            if(q[i].op)
                ans[q[i].op]=dval[l];
        return;
    }
    int mid=(r+l)>>1;
    for(int i=h;i<t;i++){
        if(q[i].op){
            int cnt=B.sum(q[i].r)-B.sum(q[i].l-1);
            if(cnt>=q[i].k) q1[nq1++]=q[i];
            else q[i].k-=cnt,q2[nq2++]=q[i];
        }else{
            if(q[i].r<dval[mid]){
                B.add(q[i].l,q[i].k);
                q1[nq1++]=q[i];
            }else q2[nq2++]=q[i];
        }
    }
    for(int i=0;i<nq1;i++) 
        if(!q1[i].op) B.add(q1[i].l,-q1[i].k);
    memcpy(q+h,q1,sizeof(Node)*nq1);
    memcpy(q+h+nq1,q2,sizeof(Node)*nq2);
    solve(h,h+nq1,l,mid);
    solve(h+nq1,t,mid,r);
}
int N,Q;
int main(){
    freopen("/home/slyfc/CppFiles/in","r",stdin);
    //freopen("/home/slyfc/CppFiles/out","w",stdout);
    while(scanf("%d",&N)==1){
        B.clear();
        memset(ans,-1,sizeof ans);
        nv=nq=0;
        for(int i=1;i<=N;i++){
            int x;
            scanf("%d",&x);
            a[i]=x;
            q[nq++]=Node(0,i,x,1,i);
            dval[nv++]=x;
        }
        scanf("%d",&Q);
        for(int i=1;i<=Q;i++){
            int x;
            scanf("%d",&x);
            if(x==1){
                int l,v;
                scanf("%d%d",&l,&v);
                q[nq++]=Node(0,l,v,1,i+N);
                q[nq++]=Node(0,l,a[l],-1,i+N);
                dval[nv++]=v,a[l]=v;
            }else{
                int l,r,k;
                scanf("%d%d%d",&l,&r,&k);
                q[nq++]=Node(i,l,r,k,i+N);
            }
        }
        sort(dval,dval+nv);
        nv=unique(dval,dval+nv)-dval;
        solve(0,nq,0,nv);
        for(int i=1;i<=Q;i++)
            if(ans[i]!=-1) printf("%d\n",ans[i]);
    }
    return 0;    
}
View Code

 动态区间第k大这个问题比较经典的做法还有树状数组套主席树,但是容易mle;还有一种块状链表的做法,但是复杂度太高了,这题肯定过不了.

你可能感兴趣的:(HDU HDOJ5412(树套树or整体二分)