2020.05.16日常總結

洛谷P6492 [COCI2010-2011 R6] STEP\color{green}{\texttt{洛谷P6492\ [COCI2010-2011 R6] STEP}}

【題意】:\color{blue}{\texttt{【題意】:}} copy from 洛谷P6492

在這裏插入圖片描述

【思路】:\color{blue}{\texttt{【思路】:}}

首先,我們爲了方便處理,把 L 抽象成一個數字 00,同理把 R 抽象成 11

數據範圍提示我們,我們需要一個時間複雜度爲 O(q×logn)O(q \times \log n) 的算法 (或者是 O(n×logq)O(n \times \log q),但是應該沒有人能把詢問給 log\log 掉) 。想到了什麼?對滴,線段樹!

線段樹的一個非常重要的操作就是 pushup 操作,也就是標記合併操作。我們考慮如何實現這個 pushup 操作。

sumosum_o 爲線段樹上 oo 號節點所表示區間最長的滿足要求的字符串。我們考慮 sumosum_o 能取到什麼值:

  • 一種比較顯然的是左右兒子的 sumsum 值的較大值。
  • 還有一種就是左兒子的結尾和右兒子的開頭合併,產生了一個新的滿足要求的字符串。

第一個非常好維護,重點在於第二個。如何方便而快捷地求出第二種情況的解呢?

顯然,單獨一個 sumsum 不能維護第二種情況。我們考慮多引入幾個變量:

  • leole_o:節點 oo 所表示區間的最左邊的字符。
  • riori_o:節點 oo 所表示區間的最右邊的字符。
  • aloal_o:節點 oo 所表示區間的最長滿足條件的前綴字符串的長度。
  • aroar_o:節點 oo 所表示區間的最長滿足條件的後綴字符串的長度。

它們有什麼用?顯然,前兩個值可以方便的告訴我們第二種情況是否存在(如果左兒子的 riri\neq 右兒子的 lele 值則有第二種情況,否則沒有)。

後兩個呢?也很顯然,當有第二種情況時,第二種情況中的字符串的長度就是左兒子的 ar+ar+ 右兒子的 alal 值。

到這裏,我們便可以 O(1)O(1) 完成 pushup 操作了。

【代碼】:\color{blue}{\texttt{【代碼】:}}

const int N=2e5+100,M=N<<2;
int sum[M],le[M],ri[M],al[M],ar[M],len[M];
inline void pushup(int o){
	if (ri[o<<1]!=le[o<<1|1]){
		sum[o]=ar[o<<1]+al[o<<1|1];
		sum[o]=max(sum[o],sum[o<<1]);
		sum[o]=max(sum[o],sum[o<<1|1]);
	}
	else sum[o]=max(sum[o<<1],sum[o<<1|1]);
	le[o]=le[o<<1];ri[o]=ri[o<<1|1];
	if (al[o<<1]==len[o<<1]&&ri[o<<1]!=le[o<<1|1])
		al[o]=al[o<<1]+al[o<<1|1];//特殊情況 
	else al[o]=al[o<<1];//否則直接繼承左兒子 
	if (ar[o<<1|1]==len[o<<1|1]&&le[o<<1|1]!=ri[o<<1])
		ar[o]=ar[o<<1|1]+ar[o<<1];//一切同上 
	else ar[o]=ar[o<<1|1];//只是變成了右兒子 
}//不能理解?不妨畫個圖試試看~
inline void init_node(int o,int t){
	sum[o]=al[o]=ar[o]=1;le[o]=ri[o]=t;
}
void build(int o,int l,int r){
	len[o]=r-l+1;//區間長度 
	if (l==r){//到達葉節點 
		init_node(o,0);
		return;
	}
	register int mid=(l+r)>>1;
	build(o<<1,l,mid);
	build(o<<1|1,mid+1,r);
	pushup(o);return;
}
void updata(int o,int l,int r,int p){
	if (l==r){
		init_node(o,!le[o]);
		return;
	}
	register int mid=(l+r)>>1;
	if (p<=mid) updata(o<<1,l,mid,p);
	else updata(o<<1|1,mid+1,r,p);
	pushup(o);return;
}
int main(){
	int n=read(),m=read();
	build(1,1,n);//建樹 
	for(int i=1;i<=m;i++){
		register int x=read();
		updata(1,1,n,x);//修改 
		printf("%d\n",sum[1]);
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章