【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