【BZOJ】1901: Zju2112 Dynamic Rankings(区间第k小+树套树)

时间:2023-03-09 20:10:05
【BZOJ】1901: Zju2112 Dynamic Rankings(区间第k小+树套树)

http://www.lydsy.com/JudgeOnline/problem.php?id=1901

【BZOJ】1901: Zju2112 Dynamic Rankings(区间第k小+树套树)

这题调了我相当长的时间,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

5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3

Sample Output

3
6

HINT

20%的数据中,m,n≤100; 40%的数据中,m,n≤1000; 100%的数据中,m,n≤10000。

Source