【題解】JSOI2008 最大數
- 題目描述
現在請求你維護一個數列,要求提供以下兩種操作:
- 查詢操作。
語法:Q L
功能:查詢當前數列中末尾L個數中的最大的數,並輸出這個數的值。
限制:L不超過當前數列的長度。(L>=0)
- 插入操作。
語法:A n
功能:將n加上t,其中t是最近一次查詢操作的答案(如果還未執行過查 詢操作,則t=0),並將所得結果對一個固定的常數D取模,將所得答案插 入到數列的末尾。
限制:n是整數(可能為負數)並且在長整範圍內。
註意:初始時數列是空的,沒有一個數。
輸入輸出格式
- 輸入格式:
第一行兩個整數,M和D,其中M表示操作的個數(M <= 200,000),D如上文中所述,滿足(0<D<2,000,000,000)
接下來的M行,每行一個字符串,描述一個具體的操作。語法如上文所述。
- 輸出格式:
對於每一個查詢操作,你應該按照順序依次輸出結果,每個結果占一行。
- 輸入輸出樣例
- 輸入樣例#1:
5 100
A 96
Q 1
A 97
Q 1
Q 2
- 輸出樣例#1:
96
93
96
- 前言:
本蒟蒻很早以前就在做這道題了,當時是在搞線段樹時搜到了這道題,第一眼看到這題我根本不覺得它與線段樹有什麽關系,於是就隨便寫了一個二分+偽單調棧,結果連樣例都過不了。。。最近又搞起了線段樹,先是看了一下黃學長的博客知道自己之前的單調棧錯在了哪裏,然後又搞了一棵線段樹,結果玄學般全部MLE???為什麽最近做題總會遇到玄學錯誤,於是學習黃學長方法,搞個數組記錄l和r就過了。
- 單調棧超級詳解:
推薦先搞懂這道題的手寫隊列方法:滑動窗口
我們搞一個數組記錄加的每一個數。在我看來,這個數組其實不能叫做棧,另一篇題解這裏可能有點表達不太準確(個人觀點),然後我們再搞一個數組,我們可以理解為將這個數組儲存一個指針,這才是一個單調棧,因為它記錄的元素從左到右是依次遞減的。我們搞一個tail記錄棧頂位置,當一個新數加進來時,我們將它與棧頂的元素做比較,如果棧頂的元素都比他小,就不斷tail--,知道刪去所有指針或找到一個比它更大的數,則停止操作,將tail++的地方存放這個新數的位置(指針)。大家可以想一想,它是要從後往前找最大的數,如果在後面已經有了一個比前面都大的數,那前面記錄的指針都可以不要了,因為從這個數倒數所有查詢的答案都是它。
然後就是查詢了,查詢我們用二分查找查到我們要的位置,有人若是認真研究了我的代碼會發現這個二分查找其實就是個lower_bound,返回大於等於這個數的第一個位置,所以它返回的就是我們需要的下標。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#define ll long long
using namespace std;
const int maxn=200010;
ll sta[maxn];
ll m,d,t=0;
int node[maxn],len=0,tail=0;
int binary_search(int x){
int l=1,r=tail;
int mid;
while(l<=r){
mid=(l+r)>>1;
if(node[mid]>x)r=mid-1;
else if(node[mid]==x)return mid;
else l=mid+1;
}
}
int main()
{
char opt[15];
freopen("bzoj_1012.in","r",stdin);
freopen("bzoj_1012.out","w",stdout);
cin>>m>>d;
while(m--){
scanf("%s",opt);
if(opt[0]==‘A‘){
ll x;
scanf("%lld",&x);
x=(x+t)%d;
sta[++len]=x;
while(tail&&sta[node[tail]]<=x)tail--;
node[++tail]=len;
}
else{
int l;
scanf("%d",&l);
if(l==0){
cout<<0<<endl;
t=0;
continue;
}
int x=binary_search(len+1-l);
cout<<sta[node[x]]<<endl;
t=sta[node[x]];
}
}
fclose(stdin);
fclose(stdout);
return 0;
}
- 線段樹做fa
有人說線段樹跑得很慢,為什麽我這裏跑得比單調棧還快???難道因為是黃 學長的玄學技巧??
我也搞不懂之前的代碼為何全部玄學MLE,就當這種新方法一種技巧吧。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn=200000;
ll maxx[maxn<<2];
int cnt=0;
int L,R,tar;
ll m,d,t=0;
int le[maxn<<2],ri[maxn<<2];
void build(int now,int l,int r)
{
le[now]=l,ri[now]=r;
if(l==r){
return ;
}
int mid=(l+r)>>1;
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
return ;
}
ll query(int now){
int l=le[now],r=ri[now];
if(L<=l&&r<=R){
return maxx[now];
}
int mid=(l+r)>>1;
ll ans=0;
if(L<=mid)ans=max(ans,query(now<<1));
if(R>mid)ans=max(ans,query(now<<1|1));
return ans;
}
void insert(int now,ll x)
{
int l=le[now],r=ri[now];
if(l==r){
maxx[now]=x;
return ;
}
int mid=(l+r)>>1;
if(tar<=mid)insert(now<<1,x);
if(mid<tar)insert(now<<1|1,x);
maxx[now]=max(maxx[now<<1],maxx[now<<1|1]);
return ;
}
int main()
{
char opt[15];
cin>>m>>d;
build(1,1,m);
while(m--)
{
scanf("%s",opt);
if(opt[0]==‘A‘){
ll x;
scanf("%lld",&x);
tar=++cnt;
insert(1,(x+t)%d);
}
else{
int l;
scanf("%d",&l);
if(l!=0)
{
L=cnt-l+1,R=cnt;
t=query(1);
}
else t=0;
cout<<t<<endl;
}
}
return 0;
}
【題解】JSOI2008 最大數