題解 CF1371F Raging Thunder
阿新 • • 發佈:2020-07-02
首先,不難發現,對於每個形如>>>...>><<...<<<
的結構,球會掉到中間的那個洞裡。我們可以形象地把>
看做向下的坡,<
看做向上的坡,那麼這個球運動的情況,就是順著坡向下滾,很好理解。我們把每個這樣的(下坡再上坡的)結構,稱作一個“坑”,那麼題目就是要詢問一段區間裡,“最大的坑”的大小。
因為是區間操作,區間查詢,我們考慮用線段樹維護。
具體來說,對線段樹上一個區間,我們維護三個東西:
- 這段區間最前面的一個坑(也可能是半個坑,也就是隻有下坡或只有上坡)。
- 這段區間最後面的一個坑(也可能是半個坑;也可能根本不存在,因為整段區間就只有一個坑,已經記錄在上一條裡了)。
- 這段區間,中間(不算最前面、最後面的坑),最大的坑的大小。
例如:><<>><>><<>>>>
這段區間。
- 最前面的坑,就是
><<
。我們記錄的時候,記錄它的下坡長度:\(1\),上坡長度:\(2\)。 - 最後面的坑,就是
>>>>
。我們記錄的時候,記錄它的下坡長度:\(4\),上坡長度:\(0\)。 - 中間的,就是指
>><>><<
這段,有:>><
和>><<
兩個坑。其中最大的是第二個,大小為\(4\)
接下來,我們要重點解決兩個問題。
問題一,是區間怎麼合併?也就是線上段樹上,當前區間有一左一右兩個兒子,怎麼把它們的資訊合併起來(\(\texttt{push_up}\))呢?發現重點,在於處理中間的拼接情況,也就是左兒子的“後面的坑”,和右耳子的“前面的坑”,如何結合。結合後,可能產生\(0\), \(1\)或\(2\)個“中間的坑”,也可能會影響到合併後的區間的“前面的坑”或“後面的坑”的形態。大力分類討論即可。
問題二,是如何實現區間反轉?我們可以在一開始建線段樹的時候,就處理好“反轉前”和“反轉後”兩套資訊。當需要把一個區間反轉時,就把這兩套資訊交換一下即可。
時間複雜度\(O(n+q\log n)\)。
參考程式碼(分類討論寫的比較麻煩,建議讀者自己實現):
//韓信帶淨化,只有你們想不到的,沒有毛隊做不到的
//大力討論就完事了,奧利給!!!
//problem:CF1371F
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const int MAXN=5e5;
int n,q;
char s[MAXN+5];
struct RangeInfo{
int left_down,left_up;
int right_down,right_up;
int ans;//除去頭尾後的最大答案
RangeInfo(){
left_down=left_up=right_down=right_up=ans=0;
}
};
RangeInfo merge(const RangeInfo& a,const RangeInfo& b){
RangeInfo res;
res.left_down=a.left_down;
res.left_up=a.left_up;
res.right_down=b.right_down;
res.right_up=b.right_up;
res.ans=max(a.ans,b.ans);
if(a.right_up){
assert(a.right_down!=0);
if(b.left_down){
res.ans=max(res.ans,a.right_down+a.right_up);
if(b.left_up){
if(b.right_down)res.ans=max(res.ans,b.left_down+b.left_up);
else{
res.right_down=b.left_down;
res.right_up=b.left_up;
}
}
else{
res.right_down=b.left_down;
}
}
else{
assert(b.left_up!=0);
if(b.right_down){
res.ans=max(res.ans,a.right_down+a.right_up+b.left_up);
}
else{
res.right_down=a.right_down;
res.right_up=a.right_up+b.left_up;
}
}
}
else{
if(a.right_down){
if(b.left_down){
if(b.left_up){
if(b.right_down)res.ans=max(res.ans,a.right_down+b.left_down+b.left_up);
else{
res.right_down=a.right_down+b.left_down;
res.right_up=b.left_up;
}
}
else{
res.right_down=a.right_down+b.left_down;
}
}
else{
assert(b.left_up!=0);
if(b.right_down)res.ans=max(res.ans,a.right_down+b.left_up);
else{
res.right_down=a.right_down;
res.right_up=b.left_up;
}
}
}
else{
if(a.left_down){
if(a.left_up){
if(b.left_down){
if(b.left_up){
if(b.right_down)res.ans=max(res.ans,b.left_down+b.left_up);
else{
res.right_down=b.left_down;
res.right_up=b.left_up;
}
}
else{
res.right_down=b.left_down;
}
}
else{
assert(b.left_up!=0);
res.left_up=a.left_up+b.left_up;
}
}
else{
res.left_down=a.left_down+b.left_down;
res.left_up=b.left_up;
}
}
else{
assert(a.left_up!=0);
if(b.left_down){
if(b.left_up){
if(b.right_down)res.ans=max(res.ans,b.left_down+b.left_up);
else{
res.right_down=b.left_down;
res.right_up=b.left_up;
}
}
else{
res.right_down=b.left_down;
}
}
else{
assert(b.left_up!=0);
res.left_up=a.left_up+b.left_up;
}
}
}
}
return res;
}
int getans(const RangeInfo& a){
int ans=a.ans;
ans=max(ans,a.left_down+a.left_up);
ans=max(ans,a.right_down+a.right_up);
return ans;
}
struct SegmentTree{
RangeInfo cur[MAXN*4+5],rev[MAXN*4+5];
bool tag[MAXN*4+5];
void push_up(int p){
cur[p]=merge(cur[p<<1],cur[p<<1|1]);
rev[p]=merge(rev[p<<1],rev[p<<1|1]);
}
void build(int p,int l,int r){
if(l==r){
if(s[l]=='>'){
cur[p].left_down=1;
rev[p].left_up=1;
}
else{
cur[p].left_up=1;
rev[p].left_down=1;
}
return;
}
int mid=(l+r)>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
push_up(p);
}
void upd(int p){
tag[p]^=1;
swap(cur[p],rev[p]);
}
void push_down(int p){
if(tag[p]){
upd(p<<1);
upd(p<<1|1);
tag[p]=0;
}
}
void modify(int p,int l,int r,int ql,int qr){
//區間反轉
if(ql<=l && qr>=r){
upd(p);
return;
}
int mid=(l+r)>>1;
push_down(p);
if(ql<=mid)modify(p<<1,l,mid,ql,qr);
if(qr>mid)modify(p<<1|1,mid+1,r,ql,qr);
push_up(p);
}
RangeInfo Q_res;
bool flag;
void query(int p,int l,int r,int ql,int qr){
if(ql<=l && qr>=r){
if(!flag){
Q_res=cur[p];
flag=1;
}
else Q_res=merge(Q_res,cur[p]);
return;
}
int mid=(l+r)>>1;
push_down(p);
if(ql<=mid)query(p<<1,l,mid,ql,qr);
if(qr>mid)query(p<<1|1,mid+1,r,ql,qr);
push_up(p);
}
RangeInfo query(int l,int r){
Q_res=RangeInfo();
flag=0;
query(1,1,n,l,r);
return Q_res;
}
}T;
int main() {
cin>>n>>q;
cin>>(s+1);
T.build(1,1,n);
while(q--){
int l,r;cin>>l>>r;
T.modify(1,1,n,l,r);
RangeInfo res=T.query(l,r);
cout<<getans(res)<<endl;
}
return 0;
}