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; }
整体二分解法:
(弱渣如我看了几个小时才看懂= =).总的来说,整体二分就是二分结果,也就是说把最后答案在一个区间内的询问一块处理.但是看完这个思路之后我想了很久,感觉操作顺序和值之间有着无法逾越的鸿沟.............然后下午开始看代码......在这里详细叙述一下我的理解:
首先肯定是离散话,然后才能按值二分答案,这个过程中,值和操作是一并处理的.比如现在做到了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; }
动态区间第k大这个问题比较经典的做法还有树状数组套主席树,但是容易mle;还有一种块状链表的做法,但是复杂度太高了,这题肯定过不了.