1. 程式人生 > >[JSOI2008]最大數(線段樹基礎)

[JSOI2008]最大數(線段樹基礎)

clu 表示 沒有 scan 輸出結果 col def 每一個 最大

題目描述

現在請求你維護一個數列,要求提供以下兩種操作:

1、 查詢操作。

語法:Q L

功能:查詢當前數列中末尾L個數中的最大的數,並輸出這個數的值。

限制: L 不超過當前數列的長度。(L > 0)

2、 插入操作。

語法:A n

功能:將 n 加上 t ,其中 t 是最近一次查詢操作的答案(如果還未執行過查詢操作,則 t=0 ),並將所得結果對一個固定的常數 D 取模,將所得答案插入到數列的末尾。

限制: n 是整數(可能為負數)並且在長整範圍內。

註意:初始時數列是空的,沒有一個數。

輸入輸出格式

輸入格式:

第一行兩個整數, MD ,其中 M 表示操作的個數 (M≤200,000)D 如上文中所述,滿足 (0<D<2,000,000,000)

接下來的 M 行,每行一個字符串,描述一個具體的操作。語法如上文所述。

輸出格式:

對於每一個查詢操作,你應該按照順序依次輸出結果,每個結果占一行。

思路:

從這道題的數據範圍來看,他只有200000次操作

從最壞情況來看,數列長最多只可能200000

所以,這道題就變成了一道簡單的線段樹

我們默認這是一棵已經開好的大小為200000的線段樹

A操作就是單點修改

Q操作就是區間查詢

每個節點維護的是當前節點及其子樹的最大值

A操作就是一個簡單的單點修改,只要記錄上一次修改的位置,+1就是要修改的位置

Q操作就是一個區間查詢,查詢該區間的最大值,只要改變return的東西就好了

代碼:

//
luogu-judger-enable-o2 #include<iostream> #include<cstdio> #define rii register int i using namespace std; struct node{ long long maxn; }x[800005]; char cz; long long v,ans,m,d,mw; long long add(int wz,long long val,int l,int r,int bh) { if(l==r) { x[bh].maxn
+=val; return x[bh].maxn; } int harf=(l+r)/2; if(wz>harf) { x[bh].maxn=max(x[bh].maxn,add(wz,val,harf+1,r,bh*2+1)); } else { x[bh].maxn=max(x[bh].maxn,add(wz,val,l,harf,bh*2)); } return x[bh].maxn; } long long ask(int l,int r,int nl,int nr,int bh) { long long ltt=0; if(l==nl&&r==nr) { return x[bh].maxn; } int half=(nl+nr)/2; if(l<=half&&r>half) { ltt=max(ltt,ask(l,half,nl,half,bh*2)); ltt=max(ltt,ask(half+1,r,half+1,nr,bh*2+1)); } else { if(l<=half) { ltt=max(ltt,ask(l,r,nl,half,bh*2)); } else { ltt=max(ltt,ask(l,r,half+1,nr,bh*2+1)); } } return ltt; } int main() { scanf("%lld%lld\n",&m,&d); for(rii=1;i<=m;i++) { scanf("%c%lld\n",&cz,&v); if(cz==Q) { ans=ask(mw-v+1,mw,1,200000,1); printf("%d\n",ans); } else { mw++; // ans=1e9+7; // r1=max(r1,v); add(mw,(ans+v)%d,1,200000,1); // cout<<x[1].maxn; } } }

[JSOI2008]最大數(線段樹基礎)