【SinGuLaRiTy-1023】 Copyright (c) SinGuLaRiTy 2017. All Rights Reserved.html
你须要写一种数据结构(可参考题目标题),来维护一些数,其中须要提供如下操做:
1. 插入一个整数x
2. 删除一个整数x(如有多个相同的数,只删除一个)
3. 查询整数x的排名(如有多个相同的数,输出最小的排名),相同的数依次排名,不并列排名
4. 查询排名为x的数,排名的概念同3
5. 求x的前驱(前驱定义为小于x,且最大的数),保证x有前驱
6. 求x的后继(后继定义为大于x,且最小的数),保证x有后继node
第一行为n,表示操做的个数(n<=500000)
下面n行每行有两个数opt和x,opt表示操做的序号(1<=opt<=6,-10^7<=x<=10^7)
大规模输入数据,创建读入优化ios
对于操做3,4,5,6每行输出一个数,表示对应答案数组
样例输入 | 样例输出 |
8 |
2 |
各类平衡树模板。ui
#include<cstdio> using namespace std; struct node { int lc,rc; int sz,v; }tree[500010]; int n,op,x,pred,succ,cnt,rt; char c; void Read(int &n) { n=0; int f=1; while(c>'9'||c<'0') { c=getchar(); if(c=='-') f=-1; } while(c<='9'&&c>='0') { n=n*10+c-'0'; c=getchar(); } n*=f; } int buf[30]; void Write(int x) { if(x<0) { putchar('-'); x=-x; } buf[0]=0; while(x) { buf[++buf[0]]=x%10; x/=10; } if(!buf[0]) { buf[0]=1; buf[1]=0; } while(buf[0]) putchar('0'+buf[buf[0]--]); putchar(10); } void zig(int &r) { int t=tree[r].lc; tree[r].lc=tree[t].rc; tree[t].rc=r; tree[r].sz=tree[tree[r].lc].sz+tree[tree[r].rc].sz+1; tree[t].sz=tree[tree[t].lc].sz+tree[tree[t].rc].sz+1; r=t; } void zag(int &r) { int t=tree[r].rc; tree[r].rc=tree[t].lc; tree[t].lc=r; tree[r].sz=tree[tree[r].lc].sz+tree[tree[r].rc].sz+1; tree[t].sz=tree[tree[t].lc].sz+tree[tree[t].rc].sz+1; r=t; } void zigzag(int &r) { zig(tree[r].rc); zag(r); } void zagzig(int &r) { zag(tree[r].lc); zig(r); } void maintain(int &r,bool flag) { if(!flag) { if(tree[tree[r].rc].sz<tree[tree[tree[r].lc].lc].sz) zig(r); else if(tree[tree[r].rc].sz<tree[tree[tree[r].lc].rc].sz) zagzig(r); else return; } else { if(tree[tree[r].lc].sz<tree[tree[tree[r].rc].rc].sz) zag(r); else if(tree[tree[r].lc].sz<tree[tree[tree[r].rc].lc].sz) zigzag(r); else return; } maintain(tree[r].lc,0); maintain(tree[r].rc,1); maintain(r,0); maintain(r,1); } void Insert(int &r,int x) { if(!r) { tree[++cnt].sz=1; tree[cnt].v=x; r=cnt; return; } tree[r].sz++; x<tree[r].v ? Insert(tree[r].lc,x) : Insert(tree[r].rc,x); maintain(r,x>=tree[r].v); } int del(int &r,int x) { int res; tree[r].sz-=1; if(tree[r].v==x||(0==tree[r].lc&&x<tree[r].v)||(0==tree[r].rc&&x>tree[r].v)) { res=tree[r].v; if(0==tree[r].lc||0==tree[r].rc) r=tree[r].lc+tree[r].rc; else tree[r].v=del(tree[r].lc,x); } else res=(x<tree[r].v ? del(tree[r].lc,x) : del(tree[r].rc,x)); return res; } void predecessor(int r,int x) { if(!r) return; if(x>tree[r].v) { pred=tree[r].v; predecessor(tree[r].rc,x); } else predecessor(tree[r].lc,x); } void successor(int r,int x) { if(!r) return; if(x<tree[r].v) { succ=tree[r].v; successor(tree[r].lc,x); } else successor(tree[r].rc,x); } int kth(int r,int x) { if(x==tree[tree[r].lc].sz+1) return tree[r].v; return (x<tree[tree[r].lc].sz+1 ? kth(tree[r].lc,x) : kth(tree[r].rc,x-tree[tree[r].lc].sz-1)); } int rnk(int r,int x) { if(!r) return 1; return (x<=tree[r].v ? rnk(tree[r].lc,x) : rnk(tree[r].rc,x)+tree[tree[r].lc].sz+1); } int main() { Read(n); for(register int i=1;i<=n;i++) { Read(op);Read(x); switch(op) { case 1: Insert(rt,x); break; case 2: del(rt,x); break; case 3: Write(rnk(rt,x)); break; case 4: Write(kth(rt,x)); break; case 5: predecessor(rt,x); Write(pred); break; default: successor(rt,x); Write(succ); break; } } return 0; }
最近,阿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个)spa
仅有一个正整数,表示一年当中全部收养了宠物的领养者的不满意程度的总和mod 1000000之后的结果。3d
样例输入 | 样例输出 |
5 |
3 |
利用set中元素的单调性进行二分查找匹配,插入复杂度logn,匹配复杂度logn 。
(对于set这种简化代码的终极武器,有必要好好研究一下)
#include<cstring> #include<cmath> #include<algorithm> #include<cstdlib> #include<cstdio> #include<set> #include<iostream> #define ll long long const int mod=1000000; const int INF=2000000000; using namespace std; int n; int ans; bool flag; set<int> T; int main() { scanf("%d",&n); T.insert(-INF); T.insert(INF); while(n--) { int tag,data; scanf("%d%d",&tag,&data); if(T.size()==2) { flag=tag; T.insert(data); } else if(tag!=flag) { int small=*--T.lower_bound(data); int big=*T.lower_bound(data); if(data-small<=big-data&&small>-INF) { ans=(ans+data-small)%mod; T.erase(small); } else { ans=(ans+big-data)%mod; T.erase(big); } } else T.insert(data); } printf("%d",ans); return 0; }
OIER公司是一家大型专业化软件公司,有着数以万计的员工。做为一名出纳员,个人任务之一即是统计每位员工的工资。
这原本是一份不错的工做,可是使人郁闷的是,咱们的老板反复无常,常常调整员工的工资。
若是他心情好,就可能把每位员工的工资加上一个相同的量。
反之,若是心情很差,就可能把他们的工资扣除一个相同的量。
我真不知道除了调工资他还作什么其它事情。
工资的频繁调整很让员工反感,尤为是集体扣除工资的时候,一旦某位员工发现本身的工资已经低于了合同规定的工资下界,他就会马上气愤地离开公司,而且不再会回来了。
每位员工的工资下界都是统一规定的。
每当一我的离开公司,我就要从电脑中把他的工资档案删去,一样,每当公司招聘了一位新员工,我就得为他新建一个工资档案。
老板常常到我这边来询问工资状况,他并不问具体某位员工的工资状况,而是问如今工资第k多的员工拿多少工资。每当这时,我就不得不对数万个员工进行一次漫长的排序,而后告诉他答案。
好了,如今你已经对个人工做了解很多了。正如你猜的那样,我想请你编一个工资统计程序。怎么样,不是很困难吧?
第一行有两个非负整数n和min。n表示下面有多少条命令,min表示工资下界。
接下来的n行,每行表示一条命令。命令能够是如下四种之一:
名称 格式 做用
I命令 I_k 新建一个工资档案,初始工资为k。若是某员工的初始工资低于工资下界,他将马上离开公司。
A命令 A_k 把每位员工的工资加上k
S命令 S_k 把每位员工的工资扣除k
F命令 F_k 查询第k多的工资
_(下划线)表示一个空格,I命令、A命令、S命令中的k是一个非负整数,F命令中的k是一个正整数。
在初始时,能够认为公司里一个员工也没有。
<数据范围>
I命令的条数不超过100000
A命令和S命令的总条数不超过100
F命令的条数不超过100000
每次工资调整的调整量不超过1000
新员工的工资不超过100000
输出文件的行数为F命令的条数加一。
对于每条F命令,你的程序要输出一行,仅包含一个整数,为当前工资第k多的员工所拿的工资数,若是k大于目前员工的数目,则输出-1。
输出文件的最后一行包含一个整数,为离开公司的员工的总数。
样例输入 | 样例输出 |
9 10 |
10 |
用splay作。
操做中较为难办的就是相同数和删除区间。
相同数解决方法:保证每一个数都不一样,用num记录其个数
删除区间[l, r]:其实是将l的前驱p,r的后继q,将p移动到q的子节点,直接删除p的右子树,而后更新便可。相比对序列操做的l 和 r可能不存在,须要特殊处理一下。
再者就是各类细节:
1. 初始工资小于MIN的不算踢出的......
2. 各类PUSHUP,PUSHDOWN注意。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define N 100010 #define ls(x) tr[x].l #define rs(x) tr[x].r #define fa(x) tr[x].fa using namespace std; struct node { int l,r,fa,size,v; }; node tr[N]; int n,m,tot,root,delta,sum; void PushUp(int x) { tr[x].size=tr[ls(x)].size+tr[rs(x)].size+1; } void zig(int x) { int y=fa(x); int z=fa(y); if(y==ls(z)) ls(z)=x; else rs(z)=x; fa(x)=z,ls(y)=rs(x),fa(y)=x,fa(rs(x))=y,rs(x)=y; PushUp(y),PushUp(x); if(y==root) root=x; } void zag(int x) { int y=fa(x); int z=fa(y); if(y==ls(z)) ls(z)=x; else rs(z)=x; fa(x)=z,rs(y)=ls(x),fa(y)=x,fa(ls(x))=y,ls(x)=y; PushUp(y),PushUp(x); if(y==root) root=x; } void Splay(int x,int d) { while(fa(x)!=d) { if(ls(fa(x))==x) zig(x); else zag(x); } } void Insert(int x) { if(!root) { root=++tot; tr[tot].v=x,tr[tot].size=1; return ; } int p=root,z; while(p) { z=p; tr[p].size++; if(x<tr[p].v)p=ls(p); else p=rs(p); } if(x<tr[z].v) ls(z)=++tot; else rs(z)=++tot; tr[tot].v=x,tr[tot].size=1,fa(tot)=z; Splay(tot,0); } int del(int &x,int f) { if(!x) return 0; int k; if(tr[x].v+delta<m) { k=del(rs(x),x)+tr[ls(x)].size+1; tr[rs(x)].size=tr[x].size-k; x=rs(x),fa(x)=f; } else { k=del(ls(x),x); tr[x].size-=k; } return k; } int query(int x,int k) { if(k<=tr[rs(x)].size) return query(rs(x),k); if(k==tr[rs(x)].size+1) return tr[x].v; return query(ls(x),k-tr[rs(x)].size-1); } int main() { cin>>n>>m; for(int i=1;i<=n;i++) { char s[10]; int x; scanf("%s%d",s,&x); if(s[0]=='I'&&x>=m) Insert(x-delta); else if(s[0]=='A') delta+=x; else if(s[0]=='S') delta-=x,sum+=del(root,0); else if(s[0]=='F'&&x>tr[root].size) puts("-1"); else if(s[0]=='F') printf("%d\n",query(root,x)+delta); } cout<<sum<<endl; return 0; }
对于操做1,2,4,5各输出一行,表示查询结果
样例输入 | 样例输出 |
9 6
4 2 2 1 9 4 0 1 1
2 1 4 3
3 4 10
2 1 4 3
1 2 5 9
4 3 9 5
5 2 8 5 |
2
4
3
4
9 |
1.n和m的数据范围:n,m<=50000
2.序列中每一个数的数据范围:[0,1e8]
3.虽然原题没有,但事实上5操做的k可能为负数
树套树标准模板,没有什么特别的。
不过值得注意的是,洛谷上的用户kczno1中指出“树套树的作法是暴力”(我也这么以为,多是我写丑了,太慢了......),而应该用二分答案跑CDQ [简易CDQ分治讲解]。下面是其思路:
对于查询k大,咱们获得左边的个数num,若是k<=num,去左边,不然k-=num,去右边,不然k<=mid分左,不然分右边。
对于查询rank,咱们只在k>mid的时候,用num更新答案。
对于查询前驱,咱们只在k>mid的时候,用左边的对应区间max更新答案(线段树维护便可)。
而对于查询后继,咱们经过将全部数取反来将后继转化为前驱处理。
此外,还有一种或许更优秀的解法:树状数组套动态加点线段树。
//因为是树形数据结构复习,固然仍是写的树套树 #include<iostream> #include<cstdio> #include<cstdlib> #define N 50005 #define M 3000005 #define inf 1000000007 using namespace std; int n,m,cnt,tmp; int a[N],l[N<<2],r[N<<2],root[N<<2]; int ls[M],rs[M],rnd[M],Size[M],sum[M],v[M]; inline int read() { int a=0,f=1; char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();} while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();} return a*f; } inline void pushup(int k) { Size[k]=Size[ls[k]]+Size[rs[k]]+sum[k]; } inline void lturn(int &k) { int t=rs[k]; rs[k]=ls[t]; ls[t]=k; pushup(k); pushup(t); k=t; } inline void rturn(int &k) { int t=ls[k]; ls[k]=rs[t]; rs[t]=k; pushup(k); pushup(t); k=t; } void Insert(int &k,int num) { if(!k) { k=++cnt; Size[k]=1; sum[k]=1; v[k]=num; rnd[k]=rand(); return; } Size[k]++; if(num==v[k]) sum[k]++; else if(num<v[k]) { Insert(ls[k],num); if(rnd[ls[k]]<rnd[k]) rturn(k); } else { Insert(rs[k],num); if(rnd[rs[k]]<rnd[k]) lturn(k); } } void pre(int k,int x,int y) { l[k]=x; r[k]=y; if(l[k]==r[k]) return; int mid=l[k]+r[k]>>1; pre(k<<1,x,mid); pre(k<<1|1,mid+1,y); } void build(int k,int id,int num) { Insert(root[k],num); if(l[k]==r[k]) return; int mid=l[k]+r[k]>>1; if(id<=mid) build(k<<1,id,num); else build(k<<1|1,id,num); } inline void ask_rank(int k,int num) { if(!k) return; if(num==v[k]) { tmp+=Size[ls[k]]; return; } else if(num<v[k]) ask_rank(ls[k],num); else { tmp+=Size[ls[k]]+sum[k]; ask_rank(rs[k],num); } } inline void get_rank(int k,int x,int y,int num) { if (l[k]==x&&r[k]==y) { ask_rank(root[k],num); return; } int mid=l[k]+r[k]>>1; if(y<=mid) get_rank(k<<1,x,y,num); else if(x>mid) get_rank(k<<1|1,x,y,num); else get_rank(k<<1,x,mid,num),get_rank(k<<1|1,mid+1,y,num); } inline void get_index(int x,int y,int z) { int l=0,r=inf,ans; while (l<=r) { int mid=l+r>>1; tmp=1; get_rank(1,x,y,mid); if(tmp<=z) ans=mid,l=mid+1; else r=mid-1; } printf("%d\n",ans); } void del(int &k,int num) { if (v[k]==num) { if(sum[k]>1) { sum[k]--; Size[k]--; return; } if(ls[k]*rs[k]==0) k=ls[k]+rs[k]; else if(rnd[ls[k]]<rnd[rs[k]]) rturn(k),del(k,num); else lturn(k),del(k,num); } else if(num<v[k]) del(ls[k],num),Size[k]--; else del(rs[k],num),Size[k]--; } inline void change(int k,int x,int num,int y) { del(root[k],y); Insert(root[k],num); if(l[k]==r[k]) return; int mid=l[k]+r[k]>>1; if(x<=mid) change(k<<1,x,num,y); else change(k<<1|1,x,num,y); } void before(int k,int num) { if(!k) return; if(v[k]<num) tmp=max(v[k],tmp),before(rs[k],num); else before(ls[k],num); } void after(int k,int num) { if(!k) return; if(v[k]>num) tmp=min(v[k],tmp),after(ls[k],num); else after(rs[k],num); } void ask_after(int k,int x,int y,int num) { if(l[k]==x&&r[k]==y) { after(root[k],num); return; } int mid=l[k]+r[k]>>1; if(y<=mid) ask_after(k<<1,x,y,num); else if(x>mid) ask_after(k<<1|1,x,y,num); else ask_after(k<<1,x,mid,num),ask_after(k<<1|1,mid+1,y,num); } void ask_before(int k,int x,int y,int num) { if(l[k]==x&&r[k]==y) { before(root[k],num); return; } int mid=l[k]+r[k]>>1; if(y<=mid) ask_before(k<<1,x,y,num); else if(x>mid) ask_before(k<<1|1,x,y,num); else ask_before(k<<1,x,mid,num),ask_before(k<<1|1,mid+1,y,num); } int main() { n=read(); m=read(); pre(1,1,n); for(int i=1;i<=n;i++) a[i]=read(),build(1,i,a[i]);; for(int i=1;i<=m;i++) { int opt=read(); int x=read(),y=read(),k; switch(opt) { case 1: k=read(); tmp=1; get_rank(1,x,y,k); printf("%d\n",tmp); break; case 2: k=read(); get_index(x,y,k); break; case 3: change(1,x,y,a[x]); a[x]=y; break; case 4: k=read(); tmp=0; ask_before(1,x,y,k); printf("%d\n",tmp); break; case 5: k=read(); tmp=inf; ask_after(1,x,y,k); printf("%d\n",tmp); break; } } return 0; }
小J是国家图书馆的一位图书管理员,他的工做是管理一个巨大的书架。虽然他很能吃苦耐劳,可是因为这个书架十分巨大,因此他的工做效率老是很低,以至他面临着被解雇的危险,这也正是他所郁闷的。
具体说来,书架由N个书位组成,编号从1到N。每一个书位放着一本书,每本书有一个特定的编码。
小J的工做有两类:
1. 图书馆常常购置新书,而书架任意时刻都是满的,因此只得将某位置的书拿掉并换成新购的书。
2. 小J须要回答顾客的查询,顾客会询问某一段连续的书位中某一特定编码的书有多少本。
例如,共5个书位,开始时书位上的书编码为1,2,3,4,5
一位顾客询问书位1到书位3中编码为“2”的书共多少本,获得的回答为:1
一位顾客询问书位1到书位3中编码为“1”的书共多少本,获得的回答为:1
此时,图书馆购进一本编码为“1”的书,并将它放到2号书位。
一位顾客询问书位1到书位3中编码为“2”的书共多少本,获得的回答为:0
一位顾客询问书位1到书位3中编码为“1”的书共多少本,获得的回答为:2
……
你的任务是写一个程序来回答每一个顾客的询问。
第一行两个整数N,M,表示一共N个书位,M个操做。
接下来一行共N个整数数A1,A2…AN,Ai表示开始时位置i上的书的编码。
接下来M行,每行表示一次操做,每行开头一个字符
若字符为‘C’,表示图书馆购进新书,后接两个整数A(1<=A<=N),P,表示这本书被放在位置A上,以及这本书的编码为P。
若字符为‘Q’,表示一个顾客的查询,后接三个整数A,B,K(1<=A<=B<=N),表示查询从第A书位到第B书位(包含A和B)中编码为K的书共多少本。
<数据范围>
对于40%的数据,1<=N,M<=5000
对于100%的数据,1<=N,M<=100000
对于100%的数据,全部出现的书的编码为不大于2147483647的正数。
对每个顾客的查询,输出一个整数,表示顾客所要查询的结果。
样例输入 | 样例输出 |
5 5 |
1
1
0
2 |
套平衡树模板,思路简单,代码较难......
#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define N 200005 #define inf 0x3f3f3f3f using namespace std; int ls[N],rs[N],h[N],data[N],wz[N],Size[N],cnt,root; int zig(int r) { int p=ls[r]; ls[r]=rs[p]; rs[p]=r; h[r]=max(h[ls[r]],h[rs[r]])+1; Size[r]=Size[ls[r]]+Size[rs[r]]+1; h[p]=max(h[ls[p]],h[rs[p]])+1; Size[p]=Size[ls[p]]+Size[rs[p]]+1; return p; } int zag(int r) { int p=rs[r]; rs[r]=ls[p]; ls[p]=r; h[r]=max(h[ls[r]],h[rs[r]])+1; Size[r]=Size[ls[r]]+Size[rs[r]]+1; h[p]=max(h[ls[p]],h[rs[p]])+1; Size[p]=Size[ls[p]]+Size[rs[p]]+1; return p; } int zagzig(int r) { ls[r]=zag(ls[r]); return zig(r); } int zigzag(int r) { rs[r]=zig(rs[r]); return zag(r); } void Move(int &r) { if(h[ls[r]]-h[rs[r]]==2) { if(h[ls[ls[r]]]>h[rs[ls[r]]]) r=zig(r); else r=zagzig(r); } else if(h[ls[r]]-h[rs[r]]==-2) { if(h[ls[rs[r]]]<h[rs[rs[r]]]) r=zag(r); else r=zigzag(r); } h[r]=max(h[ls[r]],h[rs[r]])+1; Size[r]=Size[ls[r]]+Size[rs[r]]+1; } void Insert(int a,int w,int &r) { if(!r) { r=++cnt; h[r]=1; data[r]=a; Size[r]=1; wz[r]=w; return; } if(data[r]<a||(data[r]==a&&wz[r]<w)) Insert(a,w,rs[r]); else Insert(a,w,ls[r]); Move(r); } void Delete(int a,int w,int &r) { if(!r) return; if(data[r]==a&&wz[r]==w) { if(!ls[r]||!rs[r]) { r=ls[r]+rs[r]; if(r==0) return; } else { int p=ls[r]; while(rs[p]) p=rs[p]; data[r]=data[p]; wz[r]=wz[p]; Delete(data[p],wz[p],ls[r]); } } else { if(data[r]<a||(data[r]==a&&wz[r]<w)) Delete(a,w,rs[r]); else Delete(a,w,ls[r]); } Move(r); } int getint() { int p=0,f=0; char c=getchar(); while(c<'0'||c>'9') { if(c=='-')f=1; c=getchar(); } while(c>='0'&&c<='9') { p=p*10+c-'0'; c=getchar(); } if(f)p=-p; return p; } int Find(int a,int up,int r) { if(!r) return 0; if(data[r]==a&&wz[r]==up) return Size[ls[r]]+1; else { if(data[r]<a||(data[r]==a&&wz[r]<up)) return Size[ls[r]]+1+Find(a,up,rs[r]); else return Find(a,up,ls[r]); } } int n,m,book[N]; char opt[5]; int main() { n=getint();m=getint(); for(int i=1;i<=n;i++) { book[i]=getint(); Insert(book[i],i,root); } for(int i=1;i<=m;i++) { scanf("%s",opt); if(opt[0]=='C') { int a=getint(); Delete(book[a],a,root); book[a]=getint(); Insert(book[a],a,root); } if(opt[0]=='Q') { int lef=getint(),rig=getint(),a=getint(); int p=Find(a,rig,root),q=Find(a,lef-1,root); printf("%d\n",p-q); } } return 0; }
老师交给小可可一个维护数列的任务,如今小可可但愿你来帮他完成。
有长为N的数列,不妨设为a1,a2,…,aN 。有以下三种操做形式:
(1)把数列中的一段数所有乘一个值;
(2)把数列中的一段数所有加一个值;
(3)询问数列中的一段数的和
因为答案可能很大,你只需输出这个数模P的值。
第一行两个整数N和P(1≤P≤1000000000)。
第二行含有N个非负整数,从左到右依次为a1,a2,…,aN, (0≤ai≤1000000000,1≤i≤N)。
第三行有一个整数M,表示操做总数。
从第四行开始每行描述一个操做,输入的操做有如下三种形式:
操做1:“1 t g c”(不含双引号)。表示把全部知足t≤i≤g的ai改成ai×c (1≤t≤g≤N,0≤c≤1000000000)。
操做2:“2 t g c”(不含双引号)。表示把全部知足t≤i≤g的ai改成ai+c (1≤t≤g≤N,0≤c≤1000000000)。
操做3:“3 t g”(不含双引号)。询问全部知足t≤i≤g的ai的和模P的值 (1≤t≤g≤N)。 同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。
对每一个操做3,按照它在输入中出现的顺序,依次输出一行一个整数表示询问结果。
样例输入 | 样例输出 |
7 43
1 2 3 4 5 6 7
5
1 2 5 5
3 2 4
2 3 7 9
3 1 3
3 4 7 |
2
35
8 |
很显然,本题要用线段树作,可是怎么用线段树却是个问题.能够想到先建树,每次修改一个区间的值把这个区间所包含的区间的值给修改了便可,可是这样太花时间了,有没有省时间的方法呢?咱们就要使用"懒标记"了。[忘记lazy-tag?]
简单来讲,若是要修改一个区间的值,若是这个区间恰好被彻底包含了就直接返回,打上标记(要改为多少),那么下次要用子节点的时候将标记下传便可,显然,对于本题要打两个tag,那么sum[o] = sum[o] * mul[o] + add[o],sum为和,mul为乘的数,add为加的数,那么若是咱们乘一个数c,sum[o] = sum[o] * mul[o] * c + add[o] * c,将mul[o] * c和add[o] * c看做两个总体,那么能够发现若是乘一个数c的话,add数组要*c,mul数组也要*c,同理,若是加一个数c,那么只须要add数组+c便可,由于在下传标记的时候既有加,又有减(可能为0),因此add和mul数组的计算必定要将这两种状况都计算到.
若是在线段树上几个区间操做的性质相同的,先把要求的数用操做须要的量给表示出来,而后对于每一种操做看看须要维护的标记的变化,最后合并一下便可.
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const long long maxn=1000010; long long n,p,a[maxn],add[maxn],mul[maxn],sum[maxn],m; void build(int l,int r,int o) { mul[o]=1; add[o]=0; if(l==r) { sum[o]=a[l]; return; } int mid=(l+r)>>1; build(l,mid,o*2); build(mid+1,r,o*2+1); sum[o]=(sum[o*2]+sum[o*2+1])%p; } void pushdown(int o,int k) { sum[o*2]=(sum[o*2]*mul[o]+add[o]*(k-(k>>1)))%p; sum[o*2+1]=(sum[o*2+1]*mul[o]+add[o]*(k>>1))%p; mul[o*2]=mul[o*2]*mul[o]%p; mul[o*2+1]=mul[o*2+1]*mul[o]%p; add[o*2]=(add[o*2]*mul[o]+add[o])%p; add[o*2+1]=(add[o*2+1]*mul[o]+add[o])%p; mul[o]=1; add[o]=0; } void jia(int l,int r,int o,int x,int y,int v) { if(x<=l&&r<=y) { add[o]=(add[o]+v)%p; sum[o]=(sum[o]+v*(r-l+1))%p; return; } pushdown(o,r-l+1); int mid=(l+r)>>1; if(x<=mid) jia(l,mid,o*2,x,y,v); if (y>mid) jia(mid+1,r,o*2+1,x,y,v); sum[o]=(sum[o*2]+sum[o*2+1])%p; } void cheng(int l,int r,int o,int x,int y,int v) { if(x<=l&&r<=y) { add[o]=(add[o]*v)%p; mul[o]=(mul[o]*v)%p; sum[o]=(sum[o]*v)%p; return; } pushdown(o,r-l+1); int mid=(l+r)>>1; if(x<=mid) cheng(l,mid,o*2,x,y,v); if(y>mid) cheng(mid+1,r,o*2+1,x,y,v); sum[o]=(sum[o*2]+sum[o*2+1])%p; } long long query(int l,int r,int o,int x,int y) { if(x<=l&&r<=y) return sum[o]%p; int mid=(l+r)>>1; pushdown(o,r-l+1); long long temp=0; if (x<=mid) temp=(temp+query(l,mid,o*2,x,y))%p; if (y > mid) temp=(temp+query(mid+1,r,o*2+1,x,y))%p; sum[o]=(sum[o*2]+sum[o*2+1])%p; return temp%p; } int main() { scanf("%lld%lld",&n,&p); for(int i=1;i<=n;i++) scanf("%lld",&a[i]); build(1,n,1); scanf("%lld",&m); while(m--) { int op,t,g,c; scanf("%d%d%d",&op,&t,&g); if(op==1) { scanf("%d",&c); cheng(1,n,1,t,g,c); } if(op==2) { scanf("%d",&c); jia(1,n,1,t,g,c); } if(op==3) printf("%lld\n",query(1,n,1,t,g)%p); } return 0; }
为了把工厂中高低不等的物品按从低到高排好序,工程师发明了一种排序机械臂。它遵循一个简单的排序规则,第一次操做找到摄低的物品的位置P1,并把左起第一个至P1间的物品反序;第二次找到第二低的物品的位置P2,并把左起第二个至P2间的物品反序...最终全部的物品都会被排好序。
上图给出一个示例,第一次操做前,菝低的物品在位置4,因而把第1至4的物品反序;第二次操做前,第二低的物品在位罝6,因而把第2至6的物品反序...
你的任务即是编写一个程序,肯定一个操做序列,即每次操做前第i低的物品所在位置Pi,以便机械臂工做。须要注意的是,若是有高度相同的物品,必须保证排序后它们的相对位置关系与初始时相同。
第一行包含正整数n,表示须要排序的物品数星。
第二行包含n个空格分隔的整数ai,表示每一个物品的高度。
输出一行包含n个空格分隔的整数Pi。
样例输入 | 样例输出 |
6 3 4 5 1 6 2 |
4 6 4 5 6 6 |
思路:splay 区间反转+查询
首先咱们进行一次排序,使高度小的在前,若高度相同,位置靠前的在前;而后咱们再创建平衡树,平衡树中直接存储物品的编号,即加上两个虚拟节点后,平衡树中的节点x,对应初始序列位置为x的点;平衡树的中序遍历,对应本次排序后的物品顺序。(物品的高度只在排序中有用,自建树开始高度彻底没有用了);对于输出物品位置p,将物品旋转至根节点,输出左子树大小+1便可;注意在区间反转时维护反转标记。
#include<cstdio> #include<algorithm> #define N 100005 using namespace std; int n,fa[N],siz[N],ch[N][2],root,tag[N]; struct node { int i,k; }a[N]; inline bool cmp(node p,node q) { if(p.k<q.k) return 1; if(p.k==q.k) return p.i<q.i; return 0; } inline void update(int k) { siz[k]=siz[ch[k][0]]+siz[ch[k][1]]+1; } inline void build(int l,int r,int f) { if(l>r) return; int mid=l+r>>1; if(mid<f) ch[f][0]=mid; else ch[f][1]=mid; siz[mid]=1; fa[mid]=f; if(l==r) return; build(l,mid-1,mid); build(mid+1,r,mid); update(mid); } inline void down(int k) { tag[k]^=1; if(ch[k][0]) tag[ch[k][0]]^=1; if(ch[k][1]) tag[ch[k][1]]^=1; swap(ch[k][0],ch[k][1]); } inline void rotate(int x,int & goal) { int y=fa[x],z=fa[y],kind=ch[y][1]==x; if(y==goal) goal=x; else ch[z][ch[z][1]==y]=x; ch[y][kind]=ch[x][kind^1]; ch[x][kind^1]=y; fa[y]=x;fa[x]=z; fa[ch[y][kind]]=y; update(y); } inline void splay(int x,int & goal) { while(x!=goal) { int y=fa[x],z=fa[y]; if(tag[z]) down(z); if(tag[y]) down(y); if(tag[x]) down(x); if(y!=goal) { if(ch[y][1]==x^ch[z][1]==y) rotate(x,goal); else rotate(y,goal); } rotate(x,goal); update(x); } } inline int find(int now,int k) { if(tag[now]) down(now); int l=ch[now][0],r=ch[now][1]; if(siz[l]+1==k) return now; if(k<=siz[l]) return find(l,k); return find(r,k-siz[l]-1); } inline void reverse(int l,int r) { int x=find(root,l-1),y=find(root,r+1); splay(x,root); splay(y,ch[x][1]); tag[ch[y][0]]^=1; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i+1].k); a[i+1].i=i+1; } a[1].i=1; a[n+2].i=n+2; a[n+2].k=2000000001; sort(a+1,a+n+3,cmp); build(1,n+2,0); root=n+3>>1; int p; for(int i=2;i<=n;i++) { splay(a[i].i,root); p=siz[ch[root][0]]+1; printf("%d ",p-1); reverse(i,p); } printf("%d",n); return 0; }
请写一个程序,要求维护一个数列,支持如下6种操做:(注意:格式栏中的下划线"_"表示实际输入文件中的空格)
输入的第1 行包含两个数N 和M(M ≤20 000),N表示初始时数列中数的个数,M表示要进行的操做数目。
第2行包含N个数字,描述初始时的数列。
如下M行,每行一条命令,格式参见问题描述中的表格。
任什么时候刻数列中最多含有500,000个数,数列中任何一个数字均在[-1000, 1000]内。
插入的数字总数不超过4,000,000个,输入文件大小不超过20M Bytes。
对于输入数据中的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 |
<样例解释>
初始时,咱们拥有数列:
执行操做GET-SUM_5_4,表示求出数列中第5个数字开始连续4个数字之和,即以下图所示,应为1+(-5)+(-3)+6=-1
执行操做MAX-SUM,表示求出当前数列中最大的一段和,即以下图所示,应为3+5+1+(-5)+(-3)+6+3=10
执行操做INSERT_8_3_-5_7_2,即在数列的第8个数字后插入数字-5,7,2,以下图的深色部分:
执行操做DELETE_12_1,即删除数列的第12个数字,即最后一个:
执行操做MAKE-SAME_3_3_2,表示数列中从第3个数字开始的连续3个数字,即下图所示的深色部分,所有修改成2:
改成
执行操做REVERSE_3_6,表示取出数列中从第3个数字开始的连续6个数,
如上图所示的深色部分2,2,2,-5,-3,6,翻转后获得6,-3,-5,2,2,2,并放回原来的位置:
最后执行GET-SUM_5_4和MAX-SUM,不可贵到答案1和10。
<数据规模及约定>
>你能够认为在任什么时候刻,数列中至少有1个数。
>输入数据必定是正确的,即指定位置的数在数列中必定存在。
>50%的数据中,任什么时候刻数列中最多含有30 000个数;100%的数据中,任什么时候刻>数列中最多含有500 000个数。
>100%的数据中,任什么时候刻数列中任何一个数字均在[-1000, 1000]内。
>100%的数据中,M ≤20 000,插入的数字总数不超过4 000 000个,输入文件大小不超过20M Bytes
<这道题本身没思路,因而看了看Candy?的博客,茅塞顿开啊~>
splay序列操做"裸题"。
须要回收节点编号,因此用到sz和nw()sz和nw(),一般维护序列是不用sz的;
splay维护的是这个序列,再也不存在平衡树左小右大的性质;
操做一段区间[l,r][l,r],将l-1 splay到根,r+1 splay到右儿子,他的左儿子就是要操做的区间;
为了方便加入两个哨兵节点;
注意splay和线段树不一样,每一个节点都表明了一个元素;
对于本题来讲,由于有多是修改为0或负数,因此tag=1表示执行了修改操做而不是修改为什么;
下传标记修改会覆盖查询,不管时间前后。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; #define lc t[x].ch[0] #define rc t[x].ch[1] #define pa t[x].fa typedef long long ll; const int N=5e5, INF=1e9; inline int read() { char c=getchar();int x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();} return x*f; } int n, Q, a[N], k, tot; char s[20]; struct meow { int ch[2], fa, size, v, sum, mx, lm, rm, tag, rev; meow() {} meow(int val) {ch[0]=ch[1]=fa=tag=rev=0; size=1; v=sum=mx=lm=rm=val;} }t[N]; int root, sz; inline int wh(int x) { return t[pa].ch[1]==x; } int st[N], top; inline int nw() { return top ? st[top--] : ++sz; } inline void paint(int x,int v) { t[x].tag=1; t[x].v=v; t[x].sum=t[x].size*v; t[x].mx=t[x].lm=t[x].rm=max(t[x].sum,v); t[x].rev=0; } inline void rever(int x) { if(t[x].tag) return; t[x].rev^=1; swap(lc,rc); swap(t[x].lm,t[x].rm); } inline void pushDown(int x) { if(t[x].rev) { if(lc) rever(lc); if(rc) rever(rc); t[x].rev=0; } if(t[x].tag) { if(lc) paint(lc,t[x].v); if(rc) paint(rc,t[x].v); t[x].tag=0; } } inline void update(int x) { t[x].size = t[lc].size + t[rc].size + 1; t[x].sum = t[lc].sum + t[rc].sum + t[x].v; t[x].mx = max(max(t[lc].mx, t[rc].mx), max(0, t[lc].rm) + t[x].v + max(0, t[rc].lm) ); t[x].lm = max(t[lc].lm, t[lc].sum + t[x].v + max(0, t[rc].lm) ); t[x].rm = max(t[rc].rm, t[rc].sum + t[x].v + max(0, t[lc].rm) ); } inline void rotate(int x) { int f=t[x].fa, g=t[f].fa, c=wh(x); if(g) t[g].ch[wh(f)]=x; t[x].fa=g; t[f].ch[c] = t[x].ch[c^1]; t[t[f].ch[c]].fa=f; t[x].ch[c^1]=f; t[f].fa=x; update(f); update(x); } inline void splay(int x,int tar) { for(; pa!=tar; rotate(x)) if(t[pa].fa != tar) rotate(wh(x)==wh(pa) ? pa : x); if(tar==0) root=x; } void build(int &x,int l,int r,int f) { int mid = (l+r)>>1; x=nw(); t[x]=meow(a[mid]); t[x].fa=f; if(l==r) return; if(l<mid) build(lc, l, mid-1, x); if(mid<r) build(rc, mid+1, r, x); update(x); } inline int kth(int k) { int x=root, lsize=0; while(x) { pushDown(x); int _ = lsize + t[lc].size; if(k<=_) x=lc; else if(k<=_+1) return x; else lsize=_+1, x=rc; } return -1; } void Ins(int k, int tot) { for(int i=1; i<=tot; i++) a[i]=read(); int f=kth(k+1); splay(f, 0); int x=kth(k+2); splay(x, f); build(lc, 1, tot, x); update(x); update(f); } void erase(int x) { if(!x) return; st[++top]=x; erase(lc); erase(rc); } void Del(int k, int tot) { int f=kth(k); splay(f, 0); int x=kth(k+tot+1); splay(x, f); erase(lc); lc=0; update(x); update(f); } void Mak(int k, int tot) { int f=kth(k); splay(f, 0); int x=kth(k+tot+1); splay(x, f); paint(lc, read()); update(x); update(f); } void Rev(int k, int tot) { int f=kth(k); splay(f, 0); int x=kth(k+tot+1); splay(x, f); rever(lc); update(x); update(f); } int Sum(int k, int tot) { int f=kth(k); splay(f, 0); int x=kth(k+tot+1); splay(x, f); return t[lc].sum; } int main() { n=read(); Q=read(); for(int i=2; i<=n+1; i++) a[i]=read(); a[1] = a[n+2] = -INF; t[0]=meow(-INF); t[0].sum=t[0].size=0; build(root, 1, n+2, 0); for(int i=1; i<=Q; i++) { scanf("%s",s+1); if(s[3]=='X') printf("%d\n", t[root].mx); else { k=read(); tot=read(); if(s[1]=='I') Ins(k, tot); if(s[1]=='D') Del(k, tot); if(s[1]=='M') Mak(k, tot); if(s[1]=='R') Rev(k, tot); if(s[1]=='G') printf("%d\n", Sum(k, tot)); } } return 0; }
Time: 2017-07-12