序列上相關的演算法

  • 前綴和
  • 差分
  • 單調隊列
  • 掃描線
  •  

想到甚麼就講甚麼

一些題目

好的序列

前綴和

有一長為n個陣列a,記另一個陣列把每個a的前綴(1到每個位置)的總和記起來

aka. 有一個前綴和pre,pre[i]= a[1]+...+a[i]

算法:pre[i]=pre[i-1]+a[i]

用途:區間和

若\(p_i\)=\(a_1+...+a_i\)

 

l~r的和:\(a_l+...+a_r=p_r-p_{l-1}\)

證:$$p_r=a_1+...+a_{l-1}+...+a_r$$

$$p_{l-1}=a_1+...+a_{l-1}$$

$$p_r-p_{l-1}=a_l+...+a_r$$

\(O(n)\)加值,\(O(n)\)算前綴和,\(O(1)\)區間和

例題(太多了><)

atcoder dp contest Candies

差分

對於差分陣列\(d_i=a_i-a_{i-1}\)

\(d_1=a_1\)

性質:差分陣列的前綴和即為原陣列

\(a_i=d_1+...+d_i\)

證:\(a_{i-1}+(a_i-a_{i-1})=a_i\)

用途:多筆區間修改後區間查詢

如果要在\(a_l...a_r\)都加\(x\)

先在\(d_l\)加\(x\),此時\(a_l...a_n\)都會被加\(x\),因為\(d_l\)到\(d_n\)用前綴和恢復為原陣列後都會包含到\(d_l\)

 

接下來在\(d_{r+1}\)減x,使的\(a_{r+1}...a_n\)都減回x

 

最後就只剩\(a_l...a_r\)加到x

\(O(1)\)加值,\(O(n)\)算前綴和,\(O(1)\)取值

缺點:加完每次都要\(O(n)\)前綴和,所以理想情形是當題目只需要一次加完之後再一次取值

例題

生產線

單調隊列

先看例題

用一個stack,維護一個遞減的序列

每加入一個數字x進stack,把最上面小於x的數字pop掉,直到大於x,這時最上面的數字就是最接近x且比x高的人

proof. 如果\(a_{i-1}<a_i\)那\(a_{i-1}\)一定不會是之後的高人,所以直接pop掉,之後都用不到

在pop的過程中,把某個\(a_i\) pop掉同時也pop掉所有在\(a_i\)前面且小於\(a_i\)的數,所以第一個pop不掉的數就是答案

複雜度:O(n)

proof:每個數字最多被操作到2次,第一次加到stack,第二次從stack pop掉,pop掉就不會再出現了

掃描線

將一個區間的左右接拆開,全部混再一起排序

orz

現在我們把每個區間的開頭結尾都拆開看成是數線上獨立的點

(不過要記是開頭還是結尾)

 

排序這2*n個點,從最左邊開始,遇到開頭就+1,結尾-1,每次走都取max

cnt: 0

現在我們把每個區間的開頭結尾都拆開看成是數線上獨立的點

(不過要記是開頭還是結尾)

 

排序這2*n個點,從最左邊開始,遇到開頭就+1,結尾-1,每次走都取max

cnt: 1

現在我們把每個區間的開頭結尾都拆開看成是數線上獨立的點

(不過要記是開頭還是結尾)

 

排序這2*n個點,從最左邊開始,遇到開頭就+1,結尾-1,每次走都取max

cnt: 2

現在我們把每個區間的開頭結尾都拆開看成是數線上獨立的點

(不過要記是開頭還是結尾)

 

排序這2*n個點,從最左邊開始,遇到開頭就+1,結尾-1,每次走都取max

cnt: 3

現在我們把每個區間的開頭結尾都拆開看成是數線上獨立的點

(不過要記是開頭還是結尾)

 

排序這2*n個點,從最左邊開始,遇到開頭就+1,結尾-1,每次走都取max

cnt: 2

現在我們把每個區間的開頭結尾都拆開看成是數線上獨立的點

(不過要記是開頭還是結尾)

 

排序這2*n個點,從最左邊開始,遇到開頭就+1,結尾-1,每次走都取max

cnt: 3

現在我們把每個區間的開頭結尾都拆開看成是數線上獨立的點

(不過要記是開頭還是結尾)

 

排序這2*n個點,從最左邊開始,遇到開頭就+1,結尾-1,每次走都取max

cnt: 2

現在我們把每個區間的開頭結尾都拆開看成是數線上獨立的點

(不過要記是開頭還是結尾)

 

排序這2*n個點,從最左邊開始,遇到開頭就+1,結尾-1,每次走都取max

cnt: 1

現在我們把每個區間的開頭結尾都拆開看成是數線上獨立的點

(不過要記是開頭還是結尾)

 

排序這2*n個點,從最左邊開始,遇到開頭就+1,結尾-1,每次走都取max

cnt: 0

#include <bits/stdc++.h>

using namespace std;

bool cmp(pair<int,int> a, pair<int,int> b){
	return a.first<b.first;
}

int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	int n;
	cin >> n;
	vector<pair<int,int>> save(n*2);
	for(int i=0;i<n;++i){
		cin >> save[i*2].first>> save[i*2+1].first;
		save[i*2].second=1;
		save[i*2+1].second=0;
	}
	sort(save.begin(), save.end(), cmp);
	int cur=0, ans=0;
	for(auto i:save){
		if(i.second) ++cur;
		else --cur;
		ans=max(cur, ans);
	}
	cout << ans << "\n";
}

掃描線

序列問題

By alan lai

序列問題

  • 150