@ivorysi
2018-01-02T08:54:57.000000Z
字数 40621
阅读 789
笔记
营业额统计 Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况。 Tiger拿出了公司的账本,账本上记录了公司成立以来每天的营业额。分析营业情况是一项相当复杂的工作。由于节假日,大减价或者是其他情况的时候,营业额会出现一定的波动,当然一定的波动是能够接受的,但是在某些时候营业额突变得很高或是很低,这就证明公司此时的经营状况出现了问题。经济管理学上定义了一种最小波动值来衡量这种情况: 该天的最小波动值 当最小波动值越大时,就说明营业情况越不稳定。 而分析整个公司的从成立到现在营业情况是否稳定,只需要把每一天的最小波动值加起来就可以了。你的任务就是编写一个程序帮助Tiger来计算这一个值。 第一天的最小波动值为第一天的营业额。 输入输出要求
第一行为正整数 ,表示该公司从成立一直到现在的天数,接下来的n行每行有一个整数(有可能有负数) ,表示第i
天公司的营业额。
天数n<=32767,
每天的营业额ai <= 1,000,000。
最后结果T<=2^31
输出文件仅有一个正整数,即Sigma(每天最小的波动值) 。结果小于2^31 。
6
5
1
2
5
4
6
12
splay求前驱,求后继
也就是把这个点旋转到根后,前驱是它左儿子的最右儿子,后继是它右儿子的最左儿子
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <set>
#include <map>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define MAXN 300005
//#define ivorysi
using namespace std;
typedef long long ll;
int n;
struct node {
int lc,rc,fa,val;
}tr[MAXN];
int nodecnt,root,ans;
inline void rotate(int u) {
int v=tr[u].fa;int w=tr[v].fa;
int b= (u==tr[v].lc) ? tr[u].rc : tr[u].lc;
tr[v].fa=u;tr[u].fa=w;
if(b) tr[b].fa=v;
if(w) {(v==tr[w].lc ? tr[w].lc : tr[w].rc) =u; }
if(u==tr[v].lc) {tr[u].rc=v;tr[v].lc=b;}
else {tr[u].lc=v;tr[v].rc=b;}
}
int which(int u) {
return tr[tr[u].fa].rc==u;
}
void splay(int u,int tar) {
while(tr[u].fa!=tar) {
if(tr[tr[u].fa].fa!=tar) {
if(which(u)==which(tr[u].fa)) rotate(tr[u].fa);
else rotate(u);
}
rotate(u);
}
if(!tar) root=u;
}
void insert(int val) {
int u=root,v=0,dir;
while(u) {
v=u;
if(val>tr[u].val) {
u=tr[u].rc;
dir=1;
}
else {
u=tr[u].lc;
dir=0;
}
}
u=++nodecnt;
tr[u].val=val;tr[u].fa=v;
if(dir==0) tr[v].lc=u;
else tr[v].rc=u;
splay(u,0);
}
int query() {
int res=0x7fffffff;
int suf=tr[root].rc,pre=tr[root].lc;
while(pre && tr[pre].rc) {pre=tr[pre].rc;}
while(suf && tr[suf].lc) {suf=tr[suf].lc;}
if(pre) res=min(res,tr[root].val-tr[pre].val);
if(suf) res=min(res,tr[suf].val-tr[root].val);
return res;
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
int v;
scanf("%d%d",&n,&v);
tr[++nodecnt].val=v;
ans+=v;
root=nodecnt;
xiaosiji(i,1,n) {
scanf("%d",&v);
insert(v);
ans+=query();
}
printf("%d\n",ans);
}
最近,阿Q开了一间宠物收养所。收养所提供两种服务:收养被主人遗弃的宠物和让新的主人领养这些宠物。每个领养者都希望领养到自己满意的宠物,阿Q根据领养者的要求通过他自己发明的一个特殊的公式,得出该领养者希望领养的宠物的特点值a(a是一个正整数,a<2^31),而他也给每个处在收养所的宠物一个特点值。这样他就能够很方便的处理整个领养宠物的过程了,宠物收养所总是会有两种情况发生:被遗弃的宠物过多或者是想要收养宠物的人太多,而宠物太少。 1. 被遗弃的宠物过多时,假若到来一个领养者,这个领养者希望领养的宠物的特点值为a,那么它将会领养一只目前未被领养的宠物中特点值最接近a的一只宠物。(任何两只宠物的特点值都不可能是相同的,任何两个领养者的希望领养宠物的特点值也不可能是一样的)如果有两只满足要求的宠物,即存在两只宠物他们的特点值分别为a-b和a+b,那么领养者将会领养特点值为a-b的那只宠物。 2.收养宠物的人过多,假若到来一只被收养的宠物,那么哪个领养者能够领养它呢?能够领养它的领养者,是那个希望被领养宠物的特点值最接近该宠物特点值的领养者,如果该宠物的特点值为a,存在两个领养者他们希望领养宠物的特点值分别为a-b和a+b,那么特点值为a-b的那个领养者将成功领养该宠物。 一个领养者领养了一个特点值为a的宠物,而它本身希望领养的宠物的特点值为b,那么这个领养者的不满意程度为abs(a-b)。
【任务描述】你得到了一年当中,领养者和被收养宠物到来收养所的情况,希望你计算所有收养了宠物的领养者的不满意程度的总和。这一年初始时,收养所里面既没有宠物,也没有领养者。
第一行为一个正整数n,n<=80000,表示一年当中来到收养所的宠物和领养者的总数。接下来的n行,按到来时间的先后顺序描述了一年当中来到收养所的宠物和领养者的情况。每行有两个正整数a, b,其中a=0表示宠物,a=1表示领养者,b表示宠物的特点值或是领养者希望领养宠物的特点值。(同一时间呆在收养所中的,要么全是宠物,要么全是领养者,这些宠物和领养者的个数不会超过10000个)
仅有一个正整数,表示一年当中所有收养了宠物的领养者的不满意程度的总和mod 1000000以后的结果。
5
0 2
0 4
1 3
1 2
1 5
3
(abs(3-2) + abs(2-4)=3,最后一个领养者没有宠物可以领养)
这道题如果找前驱直接把这个节点插入然后旋到根再去找前驱后继会T,直接利用二分查找树的性质去找前驱后继就好……
这里翻了个错误就是合并两个子树的时候没有把另一棵树的父亲节点更新
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <set>
#include <map>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define MAXN 300005
#define mod 1000000
//#define ivorysi
using namespace std;
typedef long long ll;
int n,t;
int val,ans,now;
struct node {
node *lc,*rc,*fa;
int val;
}*root,*pre,*suf;
int which(node *x) {
return x->fa->rc == x;
}
void rotate(node *u) {
node *v=u->fa,*w=v->fa;
node *b= (u==v->lc) ? u->rc : u->lc;
v->fa=u;u->fa=w;
if(b!=NULL) b->fa=v;
if(w!=NULL) {
(v == w->lc ? w->lc : w->rc ) = u;
}
if(u == v->rc) { v->rc = b; u->lc = v;}
else {v->lc = b; u->rc = v;}
}
void splay(node *x,node *tar) {
while(x->fa!=tar) {
if(x->fa->fa != tar) {
if(which(x) == which(x->fa)) {rotate(x->fa);}
else rotate(x);
}
rotate(x);
}
if(tar==NULL) {
root=x;
}
}
void insert(int val) {
node *x=root,*f=NULL;
int dir;
while(x!=NULL) {
f=x;
if(val > x->val) {x=x->rc;dir=1;}
else {x=x->lc;dir=0;}
}
x=new node;
x->val=val;x->fa=f;x->lc=x->rc=NULL;
if(f!=NULL) {
(dir==0 ? f->lc : f->rc) = x;
}
splay(x,NULL);
}
node* join(node *s1,node *s2) {
if(s1==NULL) return s2;
if(s2==NULL) return s1;
node *x=s1;
while(x->rc != NULL) x = x->rc;
splay(x,s1->fa);
x->rc = s2;
s2->fa = x;//忘记更新父亲了……gg
return x;
}
void del(node *x) {
splay(x,NULL);
root=join(root->lc,root->rc);
if(root!=NULL) root->fa=NULL;
}
void ask_before(node *now,int val) {
if(now==NULL) return;
if(now->val <= val) {
pre=now;
ask_before(now->rc,val);
}
else {
ask_before(now->lc,val);
}
}
void ask_after(node *now,int val) {
if(now==NULL) return;
if(now->val>=val) {
suf=now;
ask_after(now->lc,val);
}
else {
ask_after(now->rc,val);
}
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
now=-1;
scanf("%d",&n);
siji(i,1,n) {
scanf("%d%d",&t,&val);
if(now!=t && root!=NULL) {
pre=NULL;suf=NULL;
ask_before(root,val);
ask_after(root,val);
if(!pre) {ans+=(suf->val - val);del(suf);}
else if(!suf) {ans+=(val - pre->val);del(pre);}
else {
if(val - pre->val <= suf->val - val) {
ans+=(val - pre->val);
del(pre);
}
else {
ans+=(suf->val - val);
del(suf);
}
}
ans%=mod;
}
else {
insert(val);
now=t;
}
}
printf("%d\n",ans);
}
OIER公司是一家大型专业化软件公司,有着数以万计的员工。作为一名出纳员,我的任务之一便是统计每位员工的工资。这本来是一份不错的工作,但是令人郁闷的是,我们的老板反复无常,经常调整员工的工资。如果他心情好,就可能把每位员工的工资加上一个相同的量。反之,如果心情不好,就可能把他们的工资扣除一个相同的量。我真不知道除了调工资他还做什么其它事情。工资的频繁调整很让员工反感,尤其是集体扣除工资的时候,一旦某位员工发现自己的工资已经低于了合同规定的工资下界,他就会立刻气愤地离开公司,并且再也不会回来了。每位员工的工资下界都是统一规定的。每当一个人离开公司,我就要从电脑中把他的工资档案删去,同样,每当公司招聘了一位新员工,我就得为他新建一个工资档案。老板经常到我这边来询问工资情况,他并不问具体某位员工的工资情况,而是问现在工资第k多的员工拿多少工资。每当这时,我就不得不对数万个员工进行一次漫长的排序,然后告诉他答案。好了,现在你已经对我的工作了解不少了。正如你猜的那样,我想请你编一个工资统计程序。怎么样,不是很困难吧?
输出文件的行数为F命令的条数加一。对于每条F命令,你的程序要输出一行,仅包含一个整数,为当前工资第k多的员工所拿的工资数,如果k大于目前员工的数目,则输出-1。输出文件的最后一行包含一个整数,为离开公司的员工的总数。
9 10
I 60
I 70
S 50
F 2
I 30
S 15
A 5
F 1
F 2
10
20
-1
2
这道题我们需要统计一棵树的大小
我们每次增加是对整棵树增加,所以我们可以维护一个delta
加入一个人 插入一个节点k-delta
每次加工资 delta+=k
每次减工资 delta-=k
然后插入节点min-delta,旋转到根,删除左子树,再把根作为根的右儿子
查找人按照二分查找树去查找即可
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <set>
#include <map>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define MAXN 300005
#define mod 1000000
//#define ivorysi
using namespace std;
typedef long long ll;
int n,cnt;
ll minnum,delta;
char op[5];
struct node {
node *fa,*lc,*rc;
ll val;
int size;
}*root;
void update(node *u) {
u->size=1;
if(u->lc!=NULL) u->size+=u->lc->size;
if(u->rc!=NULL) u->size+=u->rc->size;
}
void rotate(node *u) {
node *v=u->fa,*w=v->fa;
node *b=(v->lc == u) ? u->rc : u->lc;
u->fa=w;v->fa=u;
if(b) b->fa=v;
if(w) {(w->lc==v ? w->lc : w->rc) = u;}
if(u==v->lc) {v->lc = b;u->rc = v;}
else {v->rc = b;u->lc = v;}
update(v);
}
int which(node *u) {
return u->fa->rc == u;
}
void splay(node *u,node *tar) {
while(u->fa!=tar) {
if(u->fa->fa!=tar) {
if(which(u) == which(u->fa)) rotate(u->fa);
else rotate(u);
}
rotate(u);
}
update(u);
if(tar==NULL) {root=u;}
}
void insert(ll val) {
node *u=root,*v=NULL;
int dir;
while(u) {
v=u;
++v->size;
if(u->val >= val) {u=u->lc;dir=0;}
else {u = u->rc; dir=1;}
}
u=new node;
u->val=val;u->fa=v;u->size=1;u->lc=u->rc=NULL;
if(v) {
if(dir==0) v->lc=u;
else v->rc=u;
}
splay(u,NULL);
}
ll query(node *now,int s) {
int k = (now->lc!=NULL ? now->lc->size : 0);
if(k+1==s) return now->val;
else if(k>=s) {
return query(now->lc,s);
}
else {
return query(now->rc,s-k-1);
}
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
scanf("%d%lld",&n,&minnum);
delta=0;
int k;
siji(i,1,n) {
scanf("%s%d",op+1,&k);
if(op[1]=='I') {
if(k<minnum) {continue;}
insert((ll)k-delta);
}
else if(op[1]=='A') {
delta+=k;
}
else if(op[1]=='S') {
delta-=k;
insert(minnum-delta);
if(root->lc!=NULL) cnt+=root->lc->size;
root->lc=NULL;
root=root->rc;
if(root!=NULL) root->fa=NULL;
}
else {
if(root==NULL || root->size < k) puts("-1");
else {
printf("%lld\n",query(root,root->size - k +1)+delta);
}
}
}
printf("%d\n",cnt);
}
小T有一个很大的书柜。这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列。她用1到n的正整数给每本书都编了号。 小T在看书的时候,每次取出一本书,看完后放回书柜然后再拿下一本。由于这些书太有吸引力了,所以她看完后常常会忘记原来是放在书柜的什么位置。不过小T的记忆力是非常好的,所以每次放书的时候至少能够将那本书放在拿出来时的位置附近,比如说她拿的时候这本书上面有X本书,那么放回去时这本书上面就只可能有X-1、X或X+1本书。 当然也有特殊情况,比如在看书的时候突然电话响了或者有朋友来访。这时候粗心的小T会随手把书放在书柜里所有书的最上面或者最下面,然后转身离开。 久而久之,小T的书柜里的书的顺序就会越来越乱,找到特定的编号的书就变得越来越困难。于是她想请你帮她编写一个图书管理程序,处理她看书时的一些操作,以及回答她的两个提问:(1)编号为X的书在书柜的什么位置;(2)从上到下第i本书的编号是多少。
第一行有两个数n,m,分别表示书的个数以及命令的条数;第二行为n个正整数:第i个数表示初始时从上至下第i个位置放置的书的编号;第三行到m+2行,每行一条命令。命令有5种形式: 1. Top S——表示把编号为S的书房在最上面。 2. Bottom S——表示把编号为S的书房在最下面。 3. Insert S T——T∈{-1,0,1},若编号为S的书上面有X本书,则这条命令表示把这本书放回去后它的上面有X+T本书; 4. Ask S——询问编号为S的书的上面目前有多少本书。 5. Query S——询问从上面数起的第S本书的编号。
对于每一条Ask或Query语句你应该输出一行,一个数,代表询问的答案。
10 10
1 3 2 7 5 8 10 4 9 6
Query 3
Top 5
Ask 6
Bottom 3
Ask 3
Top 6
Insert 4 -1
Query 5
Query 2
Ask 2
2
9
9
7
5
3
100%的数据,n,m < = 80000
top 删除这个点,然后不断的走到根的最左端
bottom 删除这个点,不断走到根的最右端
insert +1 这个点旋转到根,找到这个点的前驱,将前驱旋转为它的左儿子,将这个点插在前驱和前驱的左子树之间,这个点的右子树交给前驱
insert -1 这个点旋转到根,找到这个点的后继,将后继旋转为它的右儿子,将这个点插在后继和后继的右子树之间,这个点的左子树交给后继
ask 这个点旋转到根,左子树的大小即为答案
query 按照二分查找树的性质查找即可
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <set>
#include <map>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define MAXN 100005
#define mod 1000000
//#define ivorysi
using namespace std;
typedef long long ll;
int n,m;
int id[MAXN];
struct node {
node *fa,*lc,*rc;
int num;
int size;
}*root,*pool[MAXN];
char op[35];
void update(node *u) {
u->size=1;
if(u->lc!=NULL) u->size+=u->lc->size;
if(u->rc!=NULL) u->size+=u->rc->size;
}
int which(node *u) {
return u->fa->rc==u;
}
void rotate(node *u) {
node *v=u->fa,*w=v->fa;
node *b= (v->lc == u) ? u->rc : u->lc;
update(v);
u->fa=w;v->fa=u;
if(b) b->fa=v;
if(w) {(v==w->lc ? w->lc : w->rc) = u;}
if(v->lc==u) { v->lc=b; u->rc=v;}
else {v->rc=b; u->lc=v;}
update(v);
}
void splay(node *u,node *tar) {
while(u->fa!=tar) {
if(u->fa->fa!=tar) {
if(which(u)==which(u->fa)) rotate(u->fa);
else rotate(u);
}
rotate(u);
}
update(u);
if(tar==NULL) root=u;
}
node *build(int l,int r) {
if(l>r) return NULL;
if(l==r) return pool[id[l]];
int mid=(l+r)>>1;
node *res=pool[id[mid+1]];
res->lc=build(l,mid);
res->rc=build(mid+2,r);
if(res->lc) res->lc->fa=res;
if(res->rc) res->rc->fa=res;
update(res);
return res;
}
node* join(node *s1,node *s2) {
if(s1==NULL) return s2;
if(s2==NULL) return s1;
node *x=s1;
while(x->rc!=NULL) x=x->rc;
splay(x,s1->fa);
x->rc=s2;
s2->fa=x;
update(x);
return x;
}
void del(node *u) {
splay(u,NULL);
root=join(u->lc,u->rc);
u->fa=u->lc=u->rc=NULL;
root->fa=NULL;
}
void Top(node *u) {
del(u);
node *x=root;
while(x->lc!=NULL) {++x->size;x=x->lc;}
x->lc=u;
u->fa=x;
++x->size;
u->size=1;
}
void Bottom(node *u) {
del(u);
node *x=root;
while(x->rc!=NULL) {++x->size;x=x->rc;}
x->rc=u;
u->fa=x;
++x->size;
u->size=1;
}
void Insert_left(node *u) {
splay(u,NULL);
node *x=u->lc;
while(x->rc) x=x->rc;
splay(x,u);
u->lc=x->lc;
if(x->lc) x->lc->fa=u;
x->rc=u->rc;
if(u->rc) u->rc->fa=x;
u->rc=NULL;
x->lc=u;
u->fa=x;
root=x;
root->fa=NULL;
update(u);update(x);
}
void Insert_right(node *u) {
splay(u,NULL);
node *x=u->rc;
while(x->lc) x=x->lc;
splay(x,u);
u->rc=x->rc;
if(x->rc) x->rc->fa=u;
x->lc=u->lc;
if(u->lc) u->lc->fa=x;
u->lc=NULL;
x->rc=u;
u->fa=x;
root=x;
root->fa=NULL;
update(u);update(x);
}
int query(node *u,int s) {
int k=(u->lc!=NULL ? u->lc->size : 0);
if(k+1==s) return u->num;
else if(k>=s) {
return query(u->lc,s);
}
else {
return query(u->rc,s-k-1);
}
}
void init() {
scanf("%d%d",&n,&m);
siji(i,1,n) {
scanf("%d",&id[i]);
pool[i]=new node;
pool[i]->fa=pool[i]->lc=pool[i]->rc=NULL;
pool[i]->size=1;
pool[i]->num=i;
}
root=build(1,n);
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
init();
int s,t;
siji(i,1,m) {
scanf("%s",op+1);
scanf("%d",&s);
if(op[1]=='T') Top(pool[s]);
else if(op[1]=='B') Bottom(pool[s]);
else if(op[1]=='I') {
scanf("%d",&t);
if(t==0) continue;
if(t==-1) Insert_left(pool[s]);
if(t==1) Insert_right(pool[s]);
}
else if(op[1]=='A') {
splay(pool[s],NULL);
int res= (root->lc!=NULL ? root->lc->size : 0);
printf("%d\n",res);
}
else if(op[1]=='Q'){
printf("%d\n",query(root,s));
}
}
}
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1
第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2……n-1,n) m表示翻转操作次数
接下来m行每行两个数[l,r] 数据保证 1<=l<=r<=n
输出一行n个数字,表示原始序列经过m次变换后的结果
5 3
1 3
1 3
1 4
4 3 2 1 5
N,M<=100000
(PE是什么鬼啊!!!)
我们这回要用到打标记,打标记的精髓在于,根节点到这个节点的路上没有标记,那么结果一定会是正确的
这道题的翻转操作我们可以先设置两个哨兵节点,一前一后
我们翻转区间[l,r]可以找到第l-1个点和第r+1个点,把l-1旋转到根,r+1旋转成根的右儿子,再把r+1的左儿子打上翻转标记
我们在找l-1和r+1的点时下放翻转标记
最后查询的时候也要下放翻转标记
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <set>
#include <map>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define MAXN 100010
#define mod 1000000
//#define ivorysi
using namespace std;
typedef long long ll;
int n,m;
int ans[MAXN],tot;
struct node {
node *fa,*lc,*rc;
int size,num;
bool rev;
}*root,*pool[MAXN];
void update(node *u) {
u->size=1;
if(u->lc) u->size+=u->lc->size;
if(u->rc) u->size+=u->rc->size;
}
void pushdown(node *u) {
if(u->rev) {
swap(u->lc,u->rc);
if(u->lc) u->lc->rev ^= 1;
if(u->rc) u->rc->rev ^= 1;
u->rev=0;
}
}
node *build(int l,int r) {
if(l>r) return NULL;
if(l==r) return pool[l];
int mid=(l+r)>>1;
node *res=pool[mid+1];
res->lc=build(l,mid);
res->rc=build(mid+2,r);
if(res->lc) res->lc->fa=res;
if(res->rc) res->rc->fa=res;
update(res);
return res;
}
void rotate(node *u) {
node *v=u->fa,*w=v->fa;
node *b= (u==v->lc) ? u->rc : u->lc;
v->fa=u;u->fa=w;
if(b) b->fa=v;
if(w) {(v==w->lc ? w->lc : w->rc) = u;}
if(u==v->lc) {v->lc=b; u->rc=v;}
else {v->rc=b;u->lc=v;}
update(v);
}
int which(node *u) {
return u->fa->rc==u;
}
void splay(node *u,node *tar) {
while(u->fa!=tar) {
if(u->fa->fa!=tar) {
if(which(u)==which(u->fa)) rotate(u->fa);
else rotate(u);
}
rotate(u);
}
update(u);
if(tar==NULL) root=u;
}
node *query(node *u,int s) {
pushdown(u);
int k=(u->lc!=NULL) ? u->lc->size : 0;
if(k+1==s) {
return u;
}
else if(k>=s) {
return query(u->lc,s);
}
else {
return query(u->rc,s-k-1);
}
}
void Reverse(int l,int r) {
node *pre=query(root,l-1),*suf=query(root,r+1);
splay(pre,NULL);
splay(suf,pre);
suf->lc->rev ^=1;
}
void dfs(node *u) {
pushdown(u);
if(u->lc) dfs(u->lc);
ans[++tot]=u->num;
if(u->rc) dfs(u->rc);
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
scanf("%d%d",&n,&m);
siji(i,1,n+2) {
pool[i]=new node;
pool[i]->fa=pool[i]->lc=pool[i]->rc=NULL;
pool[i]->size=1;
pool[i]->num=i;
pool[i]->rev=0;
}
root=build(1,n+2);
int a,b;
siji(i,1,m) {
scanf("%d%d",&a,&b);
Reverse(a+1,b+1);
}
dfs(root);
siji(i,2,n+1) {
printf("%d ",ans[i]-1);
}
}
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)
第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)
对于操作3,4,5,6每行输出一个数,表示对应答案
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
106465
84185
492737
1.n的数据范围:n<=100000
2.每个数的数据范围:[-2e9,2e9]
这道题本来是一道普通的平衡树题
但是我前缀后缀写挂了
如果找到一个点这个点的右儿子还大于我们要找的数,还要继续找右儿子的左儿子……
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <set>
#include <map>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define MAXN 100010
#define mod 1000000
//#define ivorysi
using namespace std;
typedef long long ll;
struct node {
node *fa,*lc,*rc;
int cnt,val,size;
}*root;
void update(node *u) {
u->size=u->cnt;
if(u->lc) u->size+=u->lc->size;
if(u->rc) u->size+=u->rc->size;
}
void rotate(node *u) {
node *v=u->fa,*w=v->fa;
node *b=(u==v->lc) ? u->rc : u->lc;
u->fa=w;v->fa=u;
if(b) b->fa=v;
if(w) {(w->lc == v ? w->lc : w->rc)=u;}
if(u==v->lc) {v->lc=b;u->rc=v;}
else {v->rc=b;u->lc=v;}
update(v);
}
int which(node *u) {
return u->fa->rc==u;
}
void splay(node *u,node *tar) {
while(u->fa!=tar) {
if(u->fa->fa!=tar) {
if(which(u)==which(u->fa)) rotate(u->fa);
else rotate(u);
}
rotate(u);
}
update(u);
if(tar==NULL) root=u;
}
void Insert(int val) {
node *u=root,*v=NULL;
int dir;
while(u) {
v=u;
++v->size;
if(u->val == val) {++u->cnt;return;}
else if(u->val > val) {u=u->lc;dir=0;}
else {u=u->rc;dir=1;}
}
u=new node;
u->cnt=u->size=1;u->val=val;
u->fa=v;u->lc=u->rc=NULL;
if(v) {
(dir==0 ? v->lc : v->rc) =u;
}
splay(u,NULL);
}
node *findpos(node *u,int val) {
if(u->val==val) return u;
if(val < u->val) return findpos(u->lc,val);
else return findpos(u->rc,val);
}
node *join(node *s1,node *s2) {
if(s1==NULL) return s2;
if(s2==NULL) return s1;
node *x=s1;
while(x->rc) x=x->rc;
splay(x,s1->fa);
x->rc=s2;
s2->fa=x;
update(x);
return x;
}
void del(int val) {
node *x=findpos(root,val);
splay(x,NULL);
if(x->cnt>1) {--x->cnt;--x->size;}
else {
root=join(root->lc,root->rc);
if(root) root->fa=NULL;
}
}
int getpos(int val){
node *x=findpos(root,val);
splay(x,NULL);
int k= (x->lc!=NULL) ? x->lc->size : 0;
return k+1;
}
int query(node *u,int s) {
int k=(u->lc!=NULL) ? u->lc->size : 0;
if(k<s && (k + u->cnt)>=s) return u->val;
else if(k>=s) {
return query(u->lc,s);
}
else {
return query(u->rc,s- k - u->cnt);
}
}
node *pre,*suf;
void ask_prev(node *u,int val) {
if(u==NULL) return;
if(u->val < val) {
pre=u;
ask_prev(u->rc,val);
}
else {
ask_prev(u->lc,val);
}
}
void ask_suffix(node *u,int val) {
if(u==NULL) return;
if(u->val > val) {
suf=u;
ask_suffix(u->lc,val);
}
else {
ask_suffix(u->rc,val);
}
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
int n;
int op,x;
scanf("%d",&n);
siji(i,1,n) {
scanf("%d%d",&op,&x);
if(op==1) {
Insert(x);
}
else if(op==2) {
del(x);
}
else if(op==3) {
printf("%d\n",getpos(x));
}
else if(op==4) {
printf("%d\n",query(root,x));
}
else if(op==5) {
ask_prev(root,x);
printf("%d\n",pre->val);
}
else {
ask_suffix(root,x);
printf("%d\n",suf->val);
}
}
}
火星人最近研究了一种操作:求一个字串两个后缀的公共前缀。比方说,有这样一个字符串:madamimadam,
我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d a m 现在,
火星人定义了一个函数LCQ(x, y),表示:该字符串中第x个字符开始的字串,与该字符串中第y个字符开始的字串
,两个字串的公共前缀的长度。比方说,LCQ(1, 7) = 5, LCQ(2, 10) = 1, LCQ(4, 7) = 0 在研究LCQ函数的过程
中,火星人发现了这样的一个关联:如果把该字符串的所有后缀排好序,就可以很快地求出LCQ函数的值;同样,
如果求出了LCQ函数的值,也可以很快地将该字符串的后缀排好序。 尽管火星人聪明地找到了求取LCQ函数的快速
算法,但不甘心认输的地球人又给火星人出了个难题:在求取LCQ函数的同时,还可以改变字符串本身。具体地说
,可以更改字符串中某一个字符的值,也可以在字符串中的某一个位置插入一个字符。地球人想考验一下,在如此
复杂的问题中,火星人是否还能够做到很快地求取LCQ函数的值。
第一行给出初始的字符串。第二行是一个非负整数M,表示操作的个数。接下来的M行,每行描述一个操作。操作有3种,如下所示
1、询问。语法:Qxy,x,y均为正整数。功能:计算LCQ(x,y)限制:1<=x,y<=当前字符串长度。
2、修改。语法:Rxd,x是正整数,d是字符。功能:将字符串中第x个数修改为字符d。限制:x不超过当前字
符串长度。
3、插入:语法:Ixd,x是非负整数,d是字符。功能:在字符串第x个字符之后插入字符d,如果x=0,则在字
符串开头插入。限制:x不超过当前字符串长度
对于输入文件中每一个询问操作,你都应该输出对应的答案。一个答案一行。
madamimadam
7
Q 1 7
Q 4 8
Q 10 11
R 3 a
Q 1 7
I 10 a
Q 2 11
5
1
0
2
1
1、所有字符串自始至终都只有小写字母构成。
2、M<=150,000
3、字符串长度L自始至终都满足L<=100,000
4、询问操作的个数不超过10,000个。
对于第1,2个数据,字符串长度自始至终都不超过1,000
对于第3,4,5个数据,没有插入操作。
这道题我们要考虑一个问题,就是hash可以用分配律来表示
然后我们如果要得到某一个串的hash值,我们可以把一个串的前一个字符转到根,然后这个串的后一个字符转到根的右儿子,这个字符的左儿子就是答案
然后我们就可以用普通的二分求lcp的方法求lcp了,真是妙啊
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <set>
#include <map>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define MAXN 100010
#define mod 99994711
#define ba 823
//#define ivorysi
using namespace std;
typedef long long ll;
struct node {
node *fa,*lc,*rc;
int size;
ll val,c;
}*root;
char str[MAXN],op[10];
ll e[MAXN];
int n;
void update(node *u) {
u->size=1;
if(u->lc) u->size+=u->lc->size;
if(u->rc) u->size+=u->rc->size;
int k=(u->lc!=NULL) ? u->lc->size : 0;
u->val=0;
if(u->lc) u->val+=u->lc->val;
u->val+=u->c * e[k]%mod;
if(u->rc) u->val+=u->rc->val * e[k+1]%mod;
u->val%=mod;
}
node *build(int l,int r) {
if(l>r) return NULL;
node *res=new node;
res->fa=res->lc=res->rc=NULL;
res->size=1;
if(l==r) {
res->val=(str[l]-'a'+1);
res->c=(str[l]-'a'+1);
return res;
}
int mid=(l+r)>>1;
res->val=(str[mid+1]-'a'+1);
res->c=(str[mid+1]-'a'+1);
res->lc=build(l,mid);
res->rc=build(mid+2,r);
if(res->lc) res->lc->fa=res;
if(res->rc) res->rc->fa=res;
update(res);
return res;
}
void rotate(node *u) {
node *v=u->fa,*w=v->fa;
node *b= (u==v->lc) ? u->rc : u->lc;
u->fa = w;v->fa = u;
if(b) b->fa = v;//always wrong....
if(w) {(w->lc == v ? w->lc : w->rc) = u;}
if(v->lc == u) {v->lc = b;u->rc=v;}
else {v->rc =b;u->lc=v;}
update(v);
}
int which(node *u) {
return u->fa->rc==u;
}
void splay(node *u,node *tar) {
while(u->fa!=tar) {
if(u->fa->fa!=tar) {
if(which(u->fa)==which(u)) rotate(u->fa);
else rotate(u);
}
rotate(u);
}
update(u);
if(tar==NULL) root=u;
}
node *findpos(node *u,int s) {
int k=(u->lc!=NULL) ? u->lc->size : 0;
if(k+1==s) return u;
else if(k>=s) {
return findpos(u->lc,s);
}
else {
return findpos(u->rc,s-k-1);
}
}
void Insert(int x,char d) {
node *k=findpos(root,x);
splay(k,NULL);
node *p=new node;
p->fa=k;p->rc=k->rc;p->lc=NULL;
p->c=(d-'a'+1);
if(p->rc) p->rc->fa=p;
k->rc=p;
update(p);update(k);
}
void Refresh(int x,char d) {
node *k=findpos(root,x);
splay(k,NULL);
k->c = d-'a'+1;
update(k);
}
bool check(int l1,int r1,int l2,int r2) {
node *pre=findpos(root,l1-1);
node *suf=findpos(root,r1+1);
splay(pre,NULL);
splay(suf,pre);
ll res=suf->lc->val;
pre=findpos(root,l2-1);
suf=findpos(root,r2+1);
splay(pre,NULL);
splay(suf,pre);
res-=suf->lc->val;
res=(res%mod+mod)%mod;
return res==0;
}
int query(int a,int b) {
int l=0,r=root->size-max(a,b);
while(l<r) {
int mid=(l+r+1)>>1;
if(check(a,a+mid-1,b,b+mid-1)) l=mid;
else r=mid-1;
}
return l;
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
e[0]=1;
siji(i,1,100005) {
e[i]=e[i-1]*ba%mod;
}
scanf("%s",str+2);
int len=strlen(str+2);
str[1]='a';
str[len+2]='a';
root=build(1,len+2);
scanf("%d",&n);
int x,y;
char d[5];
siji(i,1,n) {
scanf("%s",op+1);
if(op[1]=='I') {
scanf("%d%s",&x,d+1);
Insert(x+1,d[1]);
}
else if(op[1]=='R') {
scanf("%d%s",&x,d+1);
Refresh(x+1,d[1]);
}
else {
scanf("%d%d",&x,&y);
printf("%d\n",query(x+1,y+1));
}
}
}
给出一棵树支持插入一些数
删除一个连续序列里的数
将一段数全部改为同一个数
将一段数反转
求区间和
求区间最大子段和
输入的第1 行包含两个数N 和M(M ≤20 000),N 表示初始时数列中数的个数,M表示要进行的操作数目。
第2行包含N个数字,描述初始时的数列。
以下M行,每行一条命令,格式参见问题描述中的表格。
任何时刻数列中最多含有500 000个数,数列中任何一个数字均在[-1 000, 1 000]内。
插入的数字总数不超过4 000 000个,输入文件大小不超过20MBytes。
对于输入数据中的GET-SUM和MAX-SUM操作,向输出文件依次打印结果,每个答案(数字)占一行。
9 8
2 -6 3 5 1 -5 -3 6 3
GET-SUM 5 4
MAX-SUM
INSERT 8 3 -5 7 2
DELETE 12 1
MAKE-SAME 3 3 2
REVERSE 3 6
GET-SUM 5 4
MAX-SUM
-1
10
1
10
这道题和文艺平衡树的反转标记还不太一样,因为左右儿子如果不反转的话还有可能影响根节点的值的大小如何更新
我们要维护父亲,左儿子,右儿子
树的大小,右侧最大值,左侧最大值,所有树的总和,子树里的最大子段和,这个结点的数是多少
是否被反转,是否被覆盖,覆盖的值是多少
插入 把区间的前一个转到根,后一个转到根的右儿子,左子树插入建好的一棵树,像建线段树那样建,这样比较平衡
删除 由于我们要回收空间,我们还要遍历一下这棵子树,然后delete掉所有
修改 打标记,注意下传的时候左右儿子都要下传,lmax和rmax改成c和c×size里较大的一个
反转 打标记,注意下传的时候左右儿子都要下传
求区间和 维护sum即可
求最大子段和 用左右子树的最大子段和,和包含根节点的最大子段和来更新
#include <iostream>
#include <cstdio>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
//#define ivorysi
using namespace std;
typedef long long ll;
struct node {
node *fa,*lc,*rc;
int size;
int rmax,lmax,sum,maxval,cover,val;
bool rev,iscover;
}*root;
char s[25];
int n,m,a[1000005];
void update(node *u) {
u->size=1;
u->sum=u->val;
u->maxval=u->val;
if(u->lc && u->lc->rmax>0) u->maxval+=u->lc->rmax;
if(u->rc && u->rc->lmax>0) u->maxval+=u->rc->lmax;
if(u->lc) {
u->size+=u->lc->size;
u->sum+=u->lc->sum;
u->maxval=max(u->maxval,u->lc->maxval);
}
if(u->rc) {
u->size+=u->rc->size;
u->sum+=u->rc->sum;
u->maxval=max(u->maxval,u->rc->maxval);
}
if(u->lc==NULL && u->rc==NULL) {
u->lmax=u->rmax=u->val;
}
else if(u->lc==NULL) {
u->lmax=max(u->val,u->val+u->rc->lmax);
u->rmax=max(u->rc->rmax,u->rc->sum+u->val);
}
else if(u->rc==NULL) {
u->rmax=max(u->val,u->val+u->lc->rmax);
u->lmax=max(u->lc->lmax,u->lc->sum+u->val);
}
else {
u->lmax=max(u->lc->sum+u->val+u->rc->lmax,u->lc->sum+u->val);
u->lmax=max(u->lmax,u->lc->lmax);
u->rmax=max(u->rc->sum+u->val+u->lc->rmax,u->rc->sum+u->val);
u->rmax=max(u->rmax,u->rc->rmax);
}
}
void pushdown(node *u) {
if(u->rev) {
swap(u->lc,u->rc);
swap(u->lmax,u->rmax);
if(u->lc) {u->lc->rev^=1;}
if(u->rc) {u->rc->rev^=1;}
u->rev=0;
}
if(u->iscover) {
u->sum=u->size*u->cover;
u->val=u->cover;
u->lmax=max(u->val,u->sum);
u->rmax=max(u->val,u->sum);
u->maxval=max(u->val,u->sum);
u->iscover=0;
if(u->lc) {u->lc->iscover=1;u->lc->cover=u->cover;}
if(u->rc) {u->rc->iscover=1;u->rc->cover=u->cover;}
}
}
node *findpos(node *u,int s) {
pushdown(u);
if(u->lc) pushdown(u->lc);
if(u->rc) pushdown(u->rc);
int k=(u->lc!=NULL) ? u->lc->size : 0;
node *res;
if(k+1==s) res=u;
else if(k>=s) {
res=findpos(u->lc,s);
}
else {
res=findpos(u->rc,s-k-1);
}
return res;
}
void rotate(node *u) {
node *v = u->fa,*w= v->fa;
node *b= (u==v->lc) ? u->rc : u->lc;
u->fa=w;v->fa=u;
if(b) b->fa=v;
if(w) {(v==w->lc ? w->lc : w->rc) = u;}
if(u==v->lc) {v->lc = b;u->rc = v;}
else {v->rc = b;u->lc = v;}
update(v);
}
int which(node *u) {
return u->fa->rc == u;
}
void splay(node *u,node *tar) {
while(u->fa!=tar) {
if(u->fa->fa!=tar) {
if(which(u->fa) == which(u)) rotate(u->fa);
else rotate(u);
}
rotate(u);
}
update(u);
if(tar==NULL) root=u;
}
node *build(int l,int r) {
if(l>r) return NULL;
node *res=new node;
res->fa=res->lc=res->rc=NULL;
res->rev=res->iscover=0;
res->size=1;
if(l==r) {
res->val=a[l];
update(res);
return res;
}
int mid=(l+r)>>1;
res->val=a[mid+1];
res->lc=build(l,mid);
res->rc=build(mid+2,r);
if(res->lc) res->lc->fa=res;
if(res->rc) res->rc->fa=res;
update(res);
return res;
}
void Insert() {
int pos,tot;
scanf("%d%d",&pos,&tot);
siji(i,1,tot) scanf("%d",&a[i]);
node *pre=findpos(root,pos+1),*suf=findpos(root,pos+2);
splay(pre,NULL);
splay(suf,pre);
if(suf==NULL) {puts("QAQ");return;}
suf->lc=build(1,tot);
if(suf->lc) suf->lc->fa=suf;
update(suf);update(pre);
}
void dfs_delete(node *u) {
if(u==NULL) return;
dfs_delete(u->lc);
dfs_delete(u->rc);
delete u;
}
void Delete() {
int pos,tot;
scanf("%d%d",&pos,&tot);
node *pre=findpos(root,pos),*suf=findpos(root,pos+tot+1);
splay(pre,NULL);
splay(suf,pre);
if(suf==NULL) {puts("QAQ");return;}
dfs_delete(suf->lc);
suf->lc=NULL;
update(suf);update(pre);
}
void Make_same() {
int pos,tot,c;
scanf("%d%d%d",&pos,&tot,&c);
node *pre=findpos(root,pos),*suf=findpos(root,pos+tot+1);
splay(pre,NULL);
splay(suf,pre);
if(suf->lc) {
suf->lc->iscover=1;
suf->lc->cover=c;
pushdown(suf->lc);
}
update(suf);update(pre);
}
void Reverse() {
int pos,tot;
scanf("%d%d",&pos,&tot);
node *pre=findpos(root,pos),*suf=findpos(root,pos+1+tot);
splay(pre,NULL);
splay(suf,pre);
if(suf->lc) {
suf->lc->rev^=1;
pushdown(suf->lc);
}
update(suf);update(pre);
}
void Get_sum() {
int pos,tot;
scanf("%d%d",&pos,&tot);
node *pre=findpos(root,pos),*suf=findpos(root,pos+1+tot);
splay(pre,NULL);
splay(suf,pre);
if(suf->lc) {
printf("%d\n",suf->lc->sum);
}
else {puts("0");}
}
void Max_sum() {
if(root==NULL) {puts("QAQ");return;}
pushdown(root);
printf("%d\n",root->maxval);
}
int main() {
scanf("%d%d",&n,&m);
a[1]=-100000;a[n+2]=-1000000;
siji(i,2,n+1) {
scanf("%d",&a[i]);
}
root=build(1,n+2);
siji(i,1,m) {
scanf("%s",s+1);
if(s[1]=='I') {
Insert();
}
else if(s[1]=='D') {
Delete();
}
else if(s[3]=='K'){
Make_same();
}
else if(s[1]=='R') {
Reverse();
}
else if(s[1]=='G') {
Get_sum();
}
else {
Max_sum();
}
}
return 0;
}
You are working for Macrohard company in data structures department. After failing your previous task about key insertion you were asked to write a new data structure that would be able to return quickly k-th order statistics in the array segment.
That is, given an array a[1...n] of different integer numbers, your program must answer a series of questions Q(i, j, k) in the form: "What would be the k-th number in a[i...j] segment, if this segment was sorted?"
For example, consider the array a = (1, 5, 2, 6, 3, 7, 4). Let the question be Q(2, 5, 3). The segment a[2...5] is (5, 2, 6, 3). If we sort this segment, we get (2, 3, 5, 6), the third number is 5, and therefore the answer to the question is 5.
The first line of the input file contains n --- the size of the array, and m --- the number of questions to answer (1 <= n <= 100 000, 1 <= m <= 5 000).
The second line contains n different integer numbers not exceeding 109 by their absolute values --- the array for which the answers should be given.
The following m lines contain question descriptions, each description consists of three numbers: i, j, and k (1 <= i <= j <= n, 1 <= k <= j - i + 1) and represents the question Q(i, j, k).
For each question output the answer to it --- the k-th number in sorted a[i...j] segment.
7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3
5
6
3
This problem has huge input,so please use c-style input(scanf,printf),or you may got time limit exceed.
这是主席树的板子题
主席树需要先把所有的数离散化,然后将线段树建在一个值域上
插入每个点,如果在左儿子就新建一个左儿子,如果在右儿子就新建一个右儿子,节点中维护的是这个值域里的数有几个
查询的时候查询一个区间的[l,r],查找以l-1和r为根的树,这个区间所含数的个数是r所在的节点的sum减去l-1所在节点的sum,我们查左儿子的大小,如果左儿子大于等于k就去左儿子,否则去右儿子
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define MAXN 100005
//#define ivorysi
using namespace std;
typedef long long ll;
int n,q,m;
int a[MAXN],b[MAXN];
struct node {
int lc,rc,sum;
}tr[MAXN*20];
int root[MAXN],nodecnt;
void dist_init() {
memcpy(b,a,sizeof(a));
sort(b+1,b+n+1);
m=unique(b+1,b+n+1)-b-1;
siji(i,1,n) {
a[i]=lower_bound(b+1,b+m+1,a[i])-b;
}
}
void Insert(int y,int &x,int l,int r,int val) {
tr[x = ++nodecnt]=tr[y];
tr[x].sum++;
if(l==r) return;
int mid=(l+r)>>1;
if(val<=mid)
Insert(tr[y].lc,tr[x].lc,l,mid,val);
else
Insert(tr[y].rc,tr[x].rc,mid+1,r,val);
}
int Query(int nl,int nr,int l,int r,int k) {
if(l==r) return l;
int delta=tr[tr[nr].lc].sum-tr[tr[nl].lc].sum;
int mid=(l+r)>>1;
if(k<=delta)
return Query(tr[nl].lc,tr[nr].lc,l,mid,k);
else
return Query(tr[nl].rc,tr[nr].rc,mid+1,r,k-delta);
}
void tree_init() {
siji(i,1,n)
Insert(root[i-1],root[i],1,m,a[i]);
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
scanf("%d%d",&n,&q);
siji(i,1,n) scanf("%d",&a[i]);
dist_init();
tree_init();
int l,r,k;
siji(i,1,q) {
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",b[Query(root[l-1],root[r],1,m,k)]);
}
return 0;
}
最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分。超级计算机中的
任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第Ei秒后结束(第Si秒和Ei秒任务也在运行
),其优先级为Pi。同一时间可能有多个任务同时执行,它们的优先级可能相同,也可能不同。调度系统会经常向
查询系统询问,第Xi秒正在运行的任务中,优先级最小的Ki个任务(即将任务按照优先级从小到大排序后取前Ki个
)的优先级之和是多少。特别的,如果Ki大于第Xi秒正在运行的任务总数,则直接回答第Xi秒正在运行的任务优先
级之和。上述所有参数均为整数,时间的范围在1到n之间(包含1和n)。
输入文件第一行包含两个空格分开的正整数m和n,分别表示任务总数和时间范围。接下来m行,每行包含三个空格
分开的正整数Si、Ei和Pi(Si≤Ei),描述一个任务。接下来n行,每行包含四个空格分开的整数Xi、Ai、Bi和Ci,
描述一次查询。查询的参数Ki需要由公式 Ki=1+(Ai*Pre+Bi) mod Ci计算得到。其中Pre表示上一次查询的结果,
对于第一次查询,Pre=1。
输出共n行,每行一个整数,表示查询结果。
4 3
1 2 6
2 3 3
1 3 2
3 3 4
3 1 3 2
1 1 3 4
2 2 4 3
2
8
11
K1 = (1*1+3)%2+1 = 1
K2 = (1*2+3)%4+1 = 2
K3 = (2*8+4)%3+1 = 3
对于100%的数据,1≤m,n,Si,Ei,Ci≤100000,0≤Ai,Bi≤100000,1≤Pi≤10000000,Xi为1到n的一个排列
这道题我们建主席树,按时间建值域线段树,维护两个值,一个是节点大小,一个是节点上的值
然而我rc写成lc了!!!!
我们建两个vector,一个是起始点,我们不断地加入,一个是终止点,我们不断地删除
查询的时候直接去树里找,然后如果k大于整个左区间就加上整个区间的总和
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define MAXN 100005
//#define ivorysi
using namespace std;
typedef long long ll;
struct node {
int lc,rc,size;
ll val;
}tr[MAXN*60];
int root[MAXN],nodecnt;
int m,n,tot;
ll num[MAXN];
vector<ll> st[MAXN],ed[MAXN];
void Insert(int y,int &x,int l,int r,int id,int on) {
tr[x = ++nodecnt]=tr[y];
tr[x].size+=on;
tr[x].val+=1LL*on*num[id];
if(l==r) return;
int mid=(l+r)>>1;
if(id<=mid) {
Insert(tr[y].lc,tr[x].lc,l,mid,id,on);
}
else {
Insert(tr[y].rc,tr[x].rc,mid+1,r,id,on);
}
}
ll Query(int x,int l,int r,int k) {
if(tr[x].size<=k) return tr[x].val;
if(l==r) return k*num[l];
int mid=(l+r)>>1;
int delta=tr[tr[x].lc].size;
if(delta>=k) {
return Query(tr[x].lc,l,mid,k);
}
else {
return tr[tr[x].lc].val+Query(tr[x].rc,mid+1,r,k-delta);
}
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
scanf("%d%d",&m,&n);
int s,e,p;
siji(i,1,m) {
scanf("%d%d%d",&s,&e,&p);
st[s].push_back(p);
ed[e+1].push_back(p);
num[i]=p;
}
sort(num+1,num+m+1);
tot=unique(num+1,num+m+1)-num-1;
siji(i,1,n) {
int s=st[i].size();
root[i]=root[i-1];
xiaosiji(j,0,s) {
int id=lower_bound(num+1,num+tot+1,st[i][j])-num;
int x=root[i];
Insert(x,root[i],1,tot,id,1);
}
s=ed[i].size();
xiaosiji(j,0,s) {
int id=lower_bound(num+1,num+tot+1,ed[i][j])-num;
int x=root[i];
Insert(x,root[i],1,tot,id,-1);
}
}
ll pr=1;
int a,b,c,k,x;
siji(i,1,n) {
scanf("%d%d%d%d",&x,&a,&b,&c);
k=1LL*(1LL*a*pr+b)%c+1;
pr = Query(root[x],1,tot,k);
printf("%lld\n",pr);
}
return 0;
}
给定一个含有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继续回答上面的问题。
第一行有两个正整数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
m,n≤10000
对于每一次询问,你都需要输出他的答案,每一个输出占单独的一行。
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
3
6
这道题是带修改的主席树
我们可以用树状数组来维护这个东西
log n个点一起向下爬树
为什么开200倍的点都RE,严重怀疑n是10^5
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define MAXN 100005
//#define ivorysi
using namespace std;
typedef long long ll;
struct node {
int lc,rc,size;
}tr[MAXN*40];
struct date {
int op,l,r,k;
int pos,t;
}p[MAXN];
int n,m,nodecnt,root[MAXN],crt[2][MAXN],tot,num[MAXN*2];
int a[MAXN],now;
char s[10];
inline int lowbit(int x) {return x&(-x);}
void Insert(int y,int &x,int l,int r,int on,int val) {
tr[x=++nodecnt]=tr[y];
tr[x].size+=on;
if(l==r) return;
int mid=(l+r)>>1;
if(val<=mid) {
Insert(tr[y].lc,tr[x].lc,l,mid,on,val);
}
else {
Insert(tr[y].rc,tr[x].rc,mid+1,r,on,val);
}
}
void add(int x,int on,int val) {
for(int i=x;i<=n;i+=lowbit(i)) Insert(root[i],root[i],1,tot,on,val);
}
int getsum(int x) {
int res=0;
for(int i=x;i>=1;i-=lowbit(i)) res+=tr[tr[crt[now][i]].lc].size;
return res;
}
int query(int ql,int qr,int k) {
now=0;
for(int i=qr;i>=1;i-=lowbit(i)) crt[now][i]=root[i];
for(int i=ql;i>=1;i-=lowbit(i)) crt[now][i]=root[i];
int l=1,r=tot;
while(l<r) {
int delta=getsum(qr)-getsum(ql);
int mid=(l+r)>>1;
if(k<=delta) {
r=mid;
for(int i=qr;i>=1;i-=lowbit(i)) crt[now^1][i]=tr[crt[now][i]].lc;
for(int i=ql;i>=1;i-=lowbit(i)) crt[now^1][i]=tr[crt[now][i]].lc;
}
else {
k-=delta;
l=mid+1;
for(int i=qr;i>=1;i-=lowbit(i)) crt[now^1][i]=tr[crt[now][i]].rc;
for(int i=ql;i>=1;i-=lowbit(i)) crt[now^1][i]=tr[crt[now][i]].rc;
}
now^=1;
}
return num[l];
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
scanf("%d%d",&n,&m);
siji(i,1,n) {
scanf("%d",&a[i]);
num[i]=a[i];
}
siji(i,1,m) {
scanf("%s",s+1);
if(s[1]=='Q') {
p[i].op=1;
scanf("%d%d%d",&p[i].l,&p[i].r,&p[i].k);
}
else {
p[i].op=2;
scanf("%d%d",&p[i].pos,&p[i].t);
num[i+n]=p[i].t;
}
}
sort(num+1,num+n+m+1);
tot=unique(num+1,num+n+m+1)-num-1;
siji(i,1,n) {
int id=lower_bound(num+1,num+tot+1,a[i])-num;
add(i,1,id);
}
siji(i,1,m) {
if(p[i].op==1) {
printf("%d\n",query(p[i].l-1,p[i].r,p[i].k));
}
else {
int id=lower_bound(num+1,num+tot+1,a[p[i].pos])-num;
add(p[i].pos,-1,id);
id=lower_bound(num+1,num+tot+1,p[i].t)-num;
add(p[i].pos,1,id);
a[p[i].pos]=p[i].t;
}
}
return 0;
}
题面大意:给出n个数和这些数的范围(10000)以内
然后给出m个询问[l,r]区间内有没有一个数的出现次数大于(r-l+1)/2
因为我们建的是值域线段树,所以如果有一个区间里的数字个数大于(r-l+1)/2那么必然,这里可能有我们要找的数,如果没有的话,那就是没有解,如果正好找到单个区间里数字个数大于(r-l+1)/2,那么就是我们想要的数字
建好主席树之后跑就行
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define MAXN 300005
//#define ivorysi
using namespace std;
typedef long long ll;
struct node {
int lc,rc,size;
}tr[MAXN*20];
int root[MAXN],nodecnt,n,m,lim,a[MAXN],pos;
void Insert(int y,int &x,int l,int r,int val) {
tr[x=++nodecnt]=tr[y];
++tr[x].size;
if(l==r) return ;
int mid=(l+r)>>1;
if(val<=mid) Insert(tr[y].lc,tr[x].lc,l,mid,val);
else Insert(tr[y].rc,tr[x].rc,mid+1,r,val);
}
bool Query(int nl,int nr,int l,int r,int x) {
if(l==r) {pos=l;return 1;}
int delta_left=tr[tr[nr].lc].size-tr[tr[nl].lc].size;
int delta_right=tr[tr[nr].rc].size-tr[tr[nl].rc].size;
int mid=(l+r)>>1;
if(delta_left > x) return Query(tr[nl].lc,tr[nr].lc,l,mid,x);
if(delta_right > x) return Query(tr[nl].rc,tr[nr].rc,mid+1,r,x);
return 0;
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
scanf("%d%d",&n,&lim);
siji(i,1,n) {
scanf("%d",&a[i]);
Insert(root[i-1],root[i],1,lim,a[i]);
}
int l,r;
scanf("%d",&m);
siji(i,1,m) {
scanf("%d%d",&l,&r);
if(Query(root[l-1],root[r],1,lim,(r-l+1)/2)) {
printf("yes %d\n",pos);
}
else {
printf("no\n");
}
}
return 0;
}
给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。
第一行两个整数N,M。
第二行有N个整数,其中第i个整数表示点i的权值。
后面N-1行每行两个整数(x,y),表示点x到点y有一条边。
最后M行每行两个整数(u,v,k),表示一组询问。
M行,表示每个询问的答案。最后一个询问不输出换行符
8 5
105 2 9 3 8 5 7 7
1 2
1 3
1 4
3 5
3 6
3 7
4 8
2 5 1
0 5 2
10 5 3
11 5 4
110 8 2
2
8
9
105
7
这道题又是强制在线……
我们的主席树的精髓在于,建一棵前缀的线段树
在树上我们也一样,我们可以建一棵根缀的线段树,我们每个节点的主席树从父亲更新就可以了
然后查询的时候找到lca,然后找到这条路径所表示的线段树(是两个线段树的和),就可以像普通第k大那样查询了
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define MAXN 100005
//#define ivorysi
using namespace std;
typedef long long ll;
struct node {
int lc,rc,size;
}tr[MAXN*20];
struct data {
int next,to;
}edge[MAXN*4];
int root[MAXN],nodecnt,n,m,val[MAXN],pos,num[MAXN],tot;
int head[MAXN],sumedge,fa[MAXN][25],dep[MAXN];
void add(int u,int v) {
edge[++sumedge].to=v;
edge[sumedge].next=head[u];
head[u]=sumedge;
}
void addtwo(int u,int v) {
add(u,v);
add(v,u);
}
void Insert(int y,int &x,int l,int r,int v) {
tr[x=++nodecnt]=tr[y];
++tr[x].size;
if(l==r) return;
int mid=(l+r)>>1;
if(v<=mid) {
Insert(tr[y].lc,tr[x].lc,l,mid,v);
}
else {
Insert(tr[y].rc,tr[x].rc,mid+1,r,v);
}
}
void dfs(int u) {
int id=lower_bound(num+1,num+tot+1,val[u])-num;
Insert(root[fa[u][0]],root[u],1,tot,id);
for(int i=head[u];i;i=edge[i].next) {
int v=edge[i].to;
if(v!=fa[u][0]) {
dep[v]=dep[u]+1;
fa[v][0]=u;
dfs(v);
}
}
}
int lca(int a,int b) {
if(dep[a]<dep[b]) swap(a,b);
int l=17;
while(dep[a]>=dep[b] && l>=0) {
if(dep[fa[a][l]]>=dep[b]) {
a=fa[a][l];
}
--l;
}
if(a==b) return a;
l=17;
while(fa[a][0]!=fa[b][0]) {
if(fa[a][l]!=fa[b][l]) {
a=fa[a][l];
b=fa[b][l];
}
--l;
}
return fa[a][0];
}
int getsum(int a,int b) {
return tr[tr[a].lc].size-tr[tr[b].lc].size;
}
int Query(int left,int left_st,int right,int right_st,int k) {
int l=1,r=tot;
while(l<r) {
int delta=getsum(left,left_st)+getsum(right,right_st);
int mid=(l+r)>>1;
if(delta>=k) {
r=mid;
left=tr[left].lc;
left_st=tr[left_st].lc;
right=tr[right].lc;
right_st=tr[right_st].lc;
}
else {
l=mid+1;
k-=delta;
left=tr[left].rc;
left_st=tr[left_st].rc;
right=tr[right].rc;
right_st=tr[right_st].rc;
}
}
if(l==r) return num[l];
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
scanf("%d%d",&n,&m);
siji(i,1,n) {
scanf("%d",&val[i]);
num[i]=val[i];
}
int u,v,k;
xiaosiji(i,1,n) {
scanf("%d%d",&u,&v);
addtwo(u,v);
}
sort(num+1,num+n+1);
tot=unique(num+1,num+n+1)-num-1;
dep[1]=1;
dfs(1);
int lastans=0;
siji(j,1,17) {
siji(i,1,n) {
fa[i][j]=fa[fa[i][j-1]][j-1];
}
}
siji(i,1,m) {
scanf("%d%d%d",&u,&v,&k);
u^=lastans;
int f=lca(u,v);
lastans=Query(root[u],root[f],root[v],root[fa[f][0]],k);
printf("%d",lastans);
if(i!=m) putchar('\n');
}
return 0;
}
一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整。给你一个
长度为n的序列s。回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。
其中a
第一行序列长度n。接下来n行按顺序给出a中的数。
接下来一行Q。然后Q行每行a,b,c,d,我们令上个询问的答案是
x(如果这是第一个询问则x=0)。
令数组q={(a+x)%n,(b+x)%n,(c+x)%n,(d+x)%n}。
将q从小到大排序之后,令真正的
要询问的a=q[0],b=q[1],c=q[2],d=q[3]。
输入保证满足条件。
第一行所谓“排过序”指的是从大到小排序!
Q行依次给出询问的答案。
5
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
271451044
271451044
969056313
0,...,19:n<=20000,Q<=25000
这道题的思路很妙
我一直觉得是左子树要是稍小一点就可以往右子树走,然而我觉得中间情况不好处理
但是我们如果已知我们想要的中位数是什么,把小于等于这个数的数设成1,把大于这个数的设成-1,如果一个子段和大于等于0就证明这个中位数可以取到
我们发现[b,c]这段必须取,我们需要[a,b)后缀最大值,(c,d]前缀最大值
这个怎么维护呢,我们把值排序,每次从前一个转移过来,root[i]表示第i大的值的线段树
可以不用离散化,因为数比较少,省的太麻烦
维护一下线段树的和,左最大值,右最大值
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define MAXN 100005
//#define ivorysi
using namespace std;
typedef long long ll;
int n,Q,b[6];
struct node{
int lc,rc,sum;
int lmax,rmax;
}tr[MAXN*20];
int root[MAXN],nodecnt;
struct data {
int val,pos;
bool operator < (const data &rhs) const{
return val<rhs.val;
}
}a[MAXN];
void update(int u) {
tr[u].sum=tr[tr[u].lc].sum+tr[tr[u].rc].sum;
tr[u].lmax=max(tr[tr[u].lc].lmax,tr[tr[u].lc].sum+tr[tr[u].rc].lmax);
tr[u].rmax=max(tr[tr[u].rc].rmax,tr[tr[u].rc].sum+tr[tr[u].lc].rmax);
}
void Insert(int y,int &x,int l,int r,int val,int on) {
tr[x=++nodecnt]=tr[y];
if(l==r) {
tr[x].sum=on;
tr[x].lmax=tr[x].rmax=max(on,0);
return;
}
int mid=(l+r)>>1;
if(mid>=val) {
Insert(tr[y].lc,tr[x].lc,l,mid,val,on);
}
else {
Insert(tr[y].rc,tr[x].rc,mid+1,r,val,on);
}
update(x);
}
void build(int u,int l,int r) {
if(l==r) {
tr[u].lmax=tr[u].rmax=tr[u].sum=1;
return;
}
int mid=(l+r)>>1;
build(tr[u].lc=++nodecnt,l,mid);
build(tr[u].rc=++nodecnt,mid+1,r);
update(u);
}
int query_sum(int u,int l,int r,int ql,int qr) {
if(l==ql && r==qr) {
return tr[u].sum;
}
int mid=(l+r)>>1;
if(qr<=mid) {
return query_sum(tr[u].lc,l,mid,ql,qr);
}
else if(ql>mid) {
return query_sum(tr[u].rc,mid+1,r,ql,qr);
}
else {
return query_sum(tr[u].lc,l,mid,ql,mid)+query_sum(tr[u].rc,mid+1,r,mid+1,qr);
}
}
int query_lmax(int u,int l,int r,int ql,int qr) {
if(l==ql && r==qr) {
return tr[u].lmax;
}
int mid=(l+r)>>1;
if(qr<=mid) {
return query_lmax(tr[u].lc,l,mid,ql,qr);
}
else if(ql>mid) {
return query_lmax(tr[u].rc,mid+1,r,ql,qr);
}
else {
return max(query_lmax(tr[u].lc,l,mid,ql,mid),query_sum(tr[u].lc,l,mid,ql,mid)+query_lmax(tr[u].rc,mid+1,r,mid+1,qr));
}
}
int query_rmax(int u,int l,int r,int ql,int qr) {
if(l==ql && r==qr) {
return tr[u].rmax;
}
int mid=(l+r)>>1;
if(qr<=mid) {
return query_rmax(tr[u].lc,l,mid,ql,qr);
}
else if(ql>mid) {
return query_rmax(tr[u].rc,mid+1,r,ql,qr);
}
else {
return max(query_rmax(tr[u].rc,mid+1,r,mid+1,qr),query_sum(tr[u].rc,mid+1,r,mid+1,qr)+query_rmax(tr[u].lc,l,mid,ql,mid));
}
}
bool check(int k,int a,int b,int c,int d) {
int tmp=query_sum(k,1,n,b,c);
if(a<=b-1)tmp+=query_rmax(k,1,n,a,b-1);
if(c+1<=d)tmp+=query_lmax(k,1,n,c+1,d);
return tmp>=0;
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
scanf("%d",&n);
siji(i,1,n) {
scanf("%d",&a[i].val);
a[i].pos=i;
}
sort(a+1,a+n+1);
build(root[1]=++nodecnt,1,n);
siji(i,2,n) {
Insert(root[i-1],root[i],1,n,a[i-1].pos,-1);
}
scanf("%d",&Q);
int lastans=0;
siji(i,1,Q) {
scanf("%d%d%d%d",&b[0],&b[1],&b[2],&b[3]);
siji(j,0,3) b[j]=(b[j]%n+lastans)%n+1;
sort(b,b+4);
int l=1,r=n;
while(l<r) {
int mid=(l+r+1)>>1;
if(check(root[mid],b[0],b[1],b[2],b[3])) {
l=mid;
}
else {
r=mid-1;
}
}
printf("%d\n",a[l].val);
lastans=a[l].val%n;
}
return 0;
}