給定乙個n個整數的序列以及乙個非負整數d,請你輸出這個序列中有多少個連續子串行(長度大於1),滿足該子串行的最大值最小值之差不大於d。
如:序列1 2 3中長度大於1的連續子串行有:
1 2
2 31 2 3
第一行包含兩個整數n,d。(1<=n<=300000, 0<=d<=2000000000)
接下來一行包含n個整數。
輸出乙個整數,表示滿足條件的連續子串行個數。
8 5
5 5 4 8 -10 10 0 1
7
滿足條件的連續子串行有:
5 5
5 5 4
5 5 4 8
5 45 4 8
4 80 1
考慮分治。
令函式solve(l, r)表示統計[l, r]中合法的連續子串行個數,答案就是solve(1, n)
。
令mid為(l+r)/2(下取整),那麼,
solve(l, r) = 0
, 當l = r,
solve(l, r) = solve(l, mid) + solve(mid + 1, r) + cal(l, r, mid)
,當 l ≠ r。
上式中cal(l, r, mid)
表示左端點在區間[l, mid]
中、右端點在區間[mid + 1, r]
中的符合要求的連續子串行數目
至於cal(l, r, mid)怎麼算,大家可以仔細思考思考。(右端點是有單調性的)
注意答案要用long long
(另外這題也可以用線性的方法做哦 ~ 有興趣去搜一搜單調棧)
小總結:
分治是乙個很重要的演算法思維,想要解決乙個大問題常常是很困難的。比如我們熟悉的,同樣是用來解決子串行問題的, 最長公共子串行問題kmp演算法。
可以分治的問題常常有這樣的特點:乙個大問題可以劃分為多個可以容易解決的小問題。最經典的就是二分了。這題跟二分很像。
想清楚每個小問題改怎麼解決 ,再想清楚每個小問題怎樣匯集成大問題的解,難題就可以輕鬆解決。
std:
#include
using namespace std;
const
int n =
300005
;//max_value:用於儲存solve函式中的字首最大值
//min_value:用於儲存solve函式中的字首最小值
int n, d, max_value[n]
, min_value[n]
;vector<
int> a;
//分治計算區間[l,r]中有多少個連續子串行滿足最大值最小值之差不大於d
//l:區間左邊界
//r:區間右邊界
//返回值:滿足條件的連續子串行的個數
long
long
solve
(int l,
int r)
//我們倒序列舉子序列的左端點i,i的取值範圍在[1,mid]
//pos表示若連續子串行的左端點是i,那麼子串行的右端點最遠能拓展到pos位置,當然pos取值範圍在[mid+1,r],一開始初始化為r
//mn是字尾最小值,mx是字尾最大值,那也就是說mn=min(a[i...mid]),mx同理
//那麼以i為左端點的連續子串行(右端點在[mid+1,r]內)個數應該優pos-mid個
int mn =
0,mx =
0, pos = r;
for(
int i=mid; i>=l && pos > mid;
--i)
return ans;
}// 求出有多少個a陣列中的連續子串行(長度大於1),滿足該子串行的最大值最小值之差不大於d
// n:a陣列的長度
// d:所給d
// a:陣列a,長度為n
// 返回值:滿足條件的連續子串行的個數
long
long
getanswer
(int n,
int d, vector<
int> a)
intmain()
清華大學演算法訓練營 等式
時間限制 2s,空間256mb 問題描述 n個變數和m個 相等 或 不相等 的約束條件,請你判定是否存在一種賦值方案滿足所有m個約束條件。輸入第一行乙個整數t,表示資料組數。t 100 接下來會有t組資料,對於每組資料 第一行是兩個整數n,m,表示變數個數和約束條件的個數。1 n,m 500000 ...
清華大學演算法訓練營 象棋
你有足夠多的象棋 車 在乙個n n的棋盤上你能放多少個 車 呢?注意,所給棋盤上有些位置不能放任何東西。同時,某一行 列 最多只能存在乙個 車 第一行為乙個正整數n。1 n 500 接下來n行,每行包含n個整數,若為0表示這個位置不能放 車 若為1表示這個位置可以放 車 輸出乙個整數,表示最多能放多...
清華大學演算法訓練營 重編碼
重編碼 priority queue 有一篇文章,文章包含 n種單詞,單詞的編號從 1 至 n,第 i 種單詞的出現次數為 w i 現在,我們要用乙個 2 進製串 即只包含 0 或 1 的串 s i 來替換第 i 種單詞,使其滿足如下要求 對於任意的 1 i,j n i j 都有 s i 不是 s ...