【BZOJ】1901: Zju2112 Dynamic Rankings(区间第k小+树套树)
http://www.lydsy.com/JudgeOnline/problem.php?id=1901
这题调了我相当长的时间,1wa1a,我是第一次写树套树,这个是树状数组套splay,在每个区间维护一棵树,然后将小于key的数量累计起来,因为这种做法不能直接找第k大,而要二分然后来判断排名是否==k-1。
ps:这里有个小地方我不能理解,是看绿云大神的代码的。就是二分那里,为什么最后还要return left,不是在二分里面当s==k-1的时候就return了吗。难道说还有其它原因??所以本题不能算ac,等绿云大神noi夺金回来了我再去问问他。。
#include <cstdio> using namespace std; const int N=10005, oo=~0u>>1; #define lowbit(x) (x&-x) struct node { node* ch[2], *fa; int key, size; node() { ch[0]=ch[1]=fa=0; key=size=0; } void pushup() { size=ch[0]->size+ch[1]->size+1; } bool d() { return fa->ch[1]==this; } void setc(node* c, bool d) { ch[d]=c; c->fa=this; } }*null; struct Splay { node* root; Splay() { root=null; } void rot(node* rt) { node* fa=rt->fa; bool d=rt->d(); fa->fa->setc(rt, fa->d()); fa->setc(rt->ch[!d], d); rt->setc(fa, !d); fa->pushup(); if(root==fa) root=rt; } node* newnode(const int &key) { node* ret=new node(); ret->key=key; ret->size=1; ret->ch[0]=ret->ch[1]=ret->fa=null; return ret; } void splay(node* rt, node* fa) { while(rt->fa!=fa) { if(rt->fa->fa==fa) rot(rt); else rt->d()==rt->fa->d()?(rot(rt->fa), rot(rt)):(rot(rt), rot(rt)); } rt->pushup(); } void insert(const int& key) { if(root==null) { root=newnode(key); return; } node* t=root; while(t->ch[key>t->key]!=null) t=t->ch[key>t->key]; node* c=newnode(key); t->setc(c, key>t->key); t->pushup(); splay(c, null); } void remove(const int &key) { node* t=root; while(t!=null && t->key!=key) t=t->ch[key>t->key]; if(t==null) return; splay(t, null); node* rt=root->ch[0]; if(rt==null) rt=root->ch[1]; else { node* m=rt->ch[1]; while(m!=null && m->ch[1]!=null) m=m->ch[1]; if(m!=null) splay(m, root); rt=root->ch[0]; rt->setc(root->ch[1], 1); } delete root; root=rt; root->fa=null; if(root!=null) root->pushup(); } int rank(const int& key) { node* t=root; int ret=0; while(t!=null) { if(t->key<key) { ret+=t->ch[0]->size+1; t=t->ch[1]; } else t=t->ch[0]; } return ret; } }*line[N], *nod[N], *q[N]; int cnt; int getrank(int key) { int ret=0; for(int i=0; i<cnt; ++i) ret+=q[i]->rank(key); return ret; } int getans(int k, int l, int r) { int r1=r, s; cnt=0; while(l<=r1) { if(l<=r1-lowbit(r1)+1) { q[cnt++]=line[r1]; r1-=lowbit(r1); } else { q[cnt++]=nod[r1]; r1--; } } int left=oo+1, right=oo; for(int i=0; i<cnt; ++i) { node* p=q[i]->root; while(p!=null) { if(p->key<left) { p=p->ch[1]; continue; } if(p->key>right) { p=p->ch[0]; continue; } s=getrank(p->key); if(s==k-1) return p->key; if(s<k-1) { left=p->key; p=p->ch[1]; } else { right=p->key; p=p->ch[0]; } } } return left; } void init() { null=new node(); null->ch[0]=null->ch[1]=null->fa=null; } int arr[N]; int main() { int n, m; init(); scanf("%d%d", &n, &m); for(int i=1; i<=n; ++i) { scanf("%d", &arr[i]); line[i]=new Splay; nod[i]=new Splay; for(int j=i-lowbit(i)+1; j<=i; ++j) line[i]->insert(arr[j]); nod[i]->insert(arr[i]); } char str[3]; int a, b, c; while(m--) { scanf("%s", str); if(str[0]=='Q') { scanf("%d%d%d", &a, &b, &c); printf("%d\n", getans(c, a, b)); } else { scanf("%d%d", &a, &b); c=a; while(c<=n) { line[c]->remove(arr[a]); line[c]->insert(b); c+=lowbit(c); } nod[a]->root->key=b; arr[a]=b; } } return 0; }
另一种做法,树状数组套主席树,比这个快多了。
http://www.cnblogs.com/iwtwiioi/p/3929957.html
Description
给定一个含有n个数的序列 a[1],a[2],a[3]……a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i+2]……a[j]中第k 小的数是多少(1≤k≤j-i+1),并且,你可以改变一些a[i]的值,改变后,程序还能针对改变后的a继续回答上面的问题。你需要编一个这样的程序, 从输入文件中读入序列a,然后读入一系列的指令,包括询问指令和修改指令。对于每一个询问指令,你必须输出正确的回答。 第一行有两个正整数n(1≤n≤10000),m(1≤m≤10000)。分别表示序列的长度和指令的个数。第二行有n个数,表示 a[1],a[2]……a[n],这些数都小于10^9。接下来的m行描述每条指令,每行的格式是下面两种格式中的一种。 Q i j k 或者 C i t Q i j k (i,j,k是数字,1≤i≤j≤n, 1≤k≤j-i+1)表示询问指令,询问a[i],a[i+1]……a[j]中第k小的数。C i t (1≤i≤n,0≤t≤10^9)表示把a[i]改变成为t。
Input
对于每一次询问,你都需要输出他的答案,每一个输出占单独的一行。
Output
Sample Input
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
Sample Output
6
HINT
20%的数据中,m,n≤100; 40%的数据中,m,n≤1000; 100%的数据中,m,n≤10000。
Source