基礎資結
Basic Data Structure
by PolarisChiba
講師介紹
姓名:李昕威
PolarisChiba
清大準大一生
就是他
課程流程
-
STL
-
並查集、BIT、線段樹
-
額外:稀疏表
-
某資料結構介紹
-
例題演練
-
額外延伸(如果有的話)
方法vs函式
method V.S function
方法函式大考驗
- 蔣立元在跳舞
- IonCamp受理報名
- 蔣立元和黃恩明報名參加IonCamp
(方法)
(方法)
(函式)
方法與函式
- 方法:描述一個物件的行為與能做的事
- 函式:一塊程式碼,用來重複呼叫的
題外話
其實方法在C++中叫做member function
在JAVA中才叫做method
emplace V.S push
push:複製一個再丟進去
emplace:把數字放進去建構出一個來
vector<pair<int,int>> vec;
vec.push_back(make_pair(1, 2));
vec.emplace_back(1, 2);Queue佇列
你說你不懂怎麼排隊?上這堂課就對了
#include<queue>
using namespace std;
queue<int> q;先進先出(FIFO)

emplace

q.emplace(2);
front
q.front();
pop
q.pop();
總結
#include<queue>
using namespace std;
queue<int> q;
q.emplace(7122);
q.front();
q.pop();-
為甚麼叫做front?
-
為甚麼不叫做IONthe1st?
-
為甚麼我不能愛叫就叫甚麼?
-
我可以自己為它取名嗎?
Stack堆疊
你說你不懂怎麼放盤子?上這堂課就對了
#include<stack>
using namespace std;
stack<int> s;後進先出(LIFO)

emplace

s.emplace(2);
top
s.top();
pop
s.pop();
總結
#include<stack>
using namespace std;
stack<int> s;
s.emplace(7122);
s.top();
s.pop();例題演練
Parentheses Balance
Parentheses Balance
(【【】(【】)()】)
給一個括號序列,請問其是否是合法的?
- 空字串是合法的
- A是合法的的話,(A)和[A]也是合法的
- A、B是合法的話,AB也是合法的
Parentheses Balance
【【】(【】)()】)
(
stack:
Parentheses Balance
【】(【】)()】)
(【
stack:
Parentheses Balance
】(【】)()】)
(【【
stack:
Parentheses Balance
(【】)()】)
(【
stack:
Parentheses Balance
【】)()】)
(【(
stack:
Parentheses Balance
】)()】)
(【(【
stack:
Parentheses Balance
)()】)
(【(
stack:
Parentheses Balance
()】)
(【
stack:
Parentheses Balance
)】)
(【(
stack:
Parentheses Balance
】)
(【
stack:
Parentheses Balance
)
(
stack:
Parentheses Balance
bool check( string line ) {
stack<char, vector<char>> sk;
for (auto x : line) {
if ( x == '(' || x == '[' ) sk.emplace(x);
else if ( x == ')' || x == ']' ) {
if ( sk.empty() ) return false;
char top = s.top();
sk.pop();
if ( top != x ) return false;
}
}
return true;
}deque雙向佇列
你說你想當個排隊達人?上這堂課就對了
#include<deque>
using namespace std;
deque<int> d;
emplace_front
d.emplace_front(2);

pop_front
d.pop_front();
總結
#include<deque>
using namespace std;
deque<int> d;
d.resize(10);
d.push_back(7122);
d.back();
d.pop_back();
d.push_front(7122);
d.front();
d.pop_front();
d[3] = 7122;例題演練
Sliding Window(簡化版)
Sliding Window(簡化版)
7 1 2 2 71 22
維護未來有可能成為答案的那些數字
(k = 3 為例)
Sliding Window(簡化版)
7 1 2 2 71 22
deque:7
Sliding Window(簡化版)
7 1 2 2 71 22
deque:7 1
Sliding Window(簡化版)
7 1 2 2 71 22
deque:7 2
Sliding Window(簡化版)
7 1 2 2 71 22
deque:2 2
Sliding Window(簡化版)
7 1 2 2 71 22
deque:71
Sliding Window(簡化版)
7 1 2 2 71 22
deque:71 22
Sliding Window(簡化版)
vector<int> solve( vector<int> a, size_t k ) {
vector<int> b;
deque<size_t> dq;
for (size_t i = 0, n = a.size(); i < n; ++i) {
while ( !dq.empty && a[i] > a[dq.back()] )
dq.pop_back();
dq.emplace_back(i);
while( dq.front() <= i - k ) dq.pop_front();
// 刪掉不符合的
// 這題先刪、還是後刪過期的都可以
if ( i > k - 2 ) b.emplace_back( a[dq.front()] );
//答案
}
return b;
}單調隊列
定義:維護一個有單調性的序列
應用:DP區間極值、DP優化
例題:求一個序列中所有長度為K的連續子序列的區
間最大值的最小值
Priority Queue優先佇列
你說想你當個比大小大師?上這堂課就對了
#include<queue>
using namespace std;
priority_queue<int> pq;Heap堆積
你說想你當個比大小大師?讓我們先成為堆積木大師吧

這一棵樹有甚麼性質呢?


蔣立元
上方的元素都比下方大
這一棵樹有甚麼性質呢?


Top:71
這一棵樹有甚麼性質呢?

Push:27
這一棵樹有甚麼性質呢?
Push:27

這一棵樹有甚麼性質呢?

Pop:71
這一棵樹有甚麼性質呢?
Pop:71

這一棵樹有甚麼性質呢?
Pop:71

這一棵樹有甚麼性質呢?
Pop:71

這一棵樹有甚麼性質呢?
Pop:71

不同的Heap
-
Min-Heap:維護最小值的堆積
-
Max-Heap:維護最大值的堆積

top
pq.top();

emplace
pq.emplace(27);

emplace
pq.emplace(27);

pop
pq.pop();

pop
pq.pop();

pop
pq.pop();

總結
#include<queue>
using namespace std;
priority_queue<int> pq;
pq.emplace(7122);
pq.top();
pq.pop();
例題演練
中位數
中位數

中位數

中位數

中位數

中位數
if ( mh.empty() || mh.top() > i ) mh.emplace(i);
else Mh.emplace(i);
if (mh.size() > Mh.size() + 1) {
Mh.emplace(mh.top());
mh.pop();
}
if (Mh.size() < mh.size() + 1) {
mh.emplace(Mh.top());
Mh.pop();
}為甚麼沒有???
你說你找不到你的祖父的手機?說不定那根本不存在
priority_queue<int> pq;
pq.clear(); // Compile Error!Priority Queue?


Priority Queue挖挖挖
Priority Queue?


Priority Queue放大鏡
Priority Queue?


Priority Queue大解密
Stack?


Stack大解密
Queue?


Queue大解密
Deque?


Deque大解密
why?
資料結構:Priority Queue、Stack、Queue
容器 :vector、deque
Stack?


Stack大解密
效能較高的他們


stack<int, vector<int>> s;
queue<int, vector<int>> q;
priority_queue<int, vector<int>> pq;less<int>
greater<int>
stack<int, vector<int>> s;
queue<int, vector<int>> q;
priority_queue<int, vector<int>> pq;
priority_queue<int, vector<int>, less<int>> pq1; // top最大,預設
priority_queue<int, vector<int>, greater<int>> pq2; // top最小Set集合
你認為人類有很多地球嗎?不,我們並沒有
#include<set>
using namespace std;
set<int> s;Red–Black Tree紅黑樹
地球上有很多棵樹,其中有一棵......

Red–Black Tree紅黑樹
How does red-black tree works?
- 各ノードは赤か黒の色をもつ。
- 根は黒である (この条件はしばし省かれる。根は赤から黒に変えることはできるので、解析にはほとんど影響しない)。
- 葉 (NIL) はすべて黒である。葉はすべて根と同じ色である。
- 赤のノードは黒ノードを 2つ子に持つ(したがって、赤のノードの親ノードは黒である)。
- 任意のノードについて、そのノードから子孫の葉までの道に含まれる黒いノードの数は、選んだ葉によらず一定である(この条件は、「根から葉までの道に含まれる黒いノードの数は、葉によらず一定である」と言い換えることができる)。
好像有點複雜餒
你說你也這麼覺得?
Binary Search Tree

二元搜尋樹

二元搜尋樹
-
左邊的都比根小 -
右邊的都比根大 -
根和根一樣大

set<int> s;find

s.find(17);insert

s.insert(25);erase

s.erase(17);

begin
s.begin();
rbegin
s.rbegin();
總結
#include<set>
using namespace std;
set<int> s;
s.insert(7122);
s.begin(); // pointer
*s.rbegin(); // integer
s.end(); // NULL
s.erase(7122);
s.erase(s.begin());
s.erase(s.find(7122));額外延伸
multiet重集
你認為人類有很多地球嗎?......可能真的有唷
multiset
multiset<int> s;
由大到小
set<int, greater<int>> s;

簡單圖判定
給定N個點和M條邊,請問這張圖是不是簡單圖?
不包含重邊
不包含自環
簡單圖判定
bool check(vector<pair<int,int>> &v) {
set<pair<int,int>> s;
for (auto i:v) {
if (i.first == i.second) return false;
s.insert(i);
}
if (v.size() != s.size()) return false;
return true;
}Map映射
你說你無法將朋友的臉和名字對上嗎?來上這堂課就對了
#include<map>
using namespace std;
map<int, string> m;Binary Search Tree

二元搜尋樹

題外話,競賽中朋友、同學很重要
map映射

從數字到字串的映射
map映射
從字串到數字的映射

map映射
map挖挖挖

erase

m.erase("CSY");
insert
m["joylintp"] = 1;
修改
m["joylintp"] = -1;

總結
#include<map>
using namespace std;
map<string, int> m;
m["CSY"] = 1;
m.erase(m.find("CSY"));
m.begin(); // pointer
*m.rbegin(); // pair
m.begin()->first // string
m.rbegin()->second // int題外話 : /

例題演練
動態眾數問題
動態眾數問題
每次加入一個數字,問目前的眾數是誰?
如果有多個的話請輸出最大的
ex:1, 3, 2, 5, 3, 2, 3, 2, 2, 1, 5
ex:1, 3, 3, 5, 3, 3, 3, 3, 2, 2, 2
動態眾數問題
void mode(vector<int> &v) {
map<int,int> m;
int ans;
for (auto i:v) {
m[i]++;
if (m[ans] < m[i]) ans = i;
else if (m[ans] == m[i] && ans < i) ans = i;
cout << i << endl;
}
}雜湊表
你嫌你不夠快嗎?來上這堂課就對了
unordered_map<int, string> m;
unordered_set<int> s;
unordered_multiset<int> ss;
もっと、速く
hash碰撞
雜湊:將資料打亂混和

STL例題演練
如果時間不夠的話,就會先換到下個章節
給你 N 個數字,每次你可以選兩個數字 X、Y ,並花費 X+Y 元將兩者合併變成 X+Y ,請問最少要花多少元才能合併到只剩一個數字?
手續費(fee)
把所有數字丟進 Priority Queue 中
並每次取出最小的兩個
再把它們相加後放回去
是不是很想要每次取出最小的兩個相加?
手續費(fee)
手續費(fee)
int fee(vector<int> &v) {
priority_queue<int, vector<int>, greater<int>> pq;
for (auto i:v) pq.emplace(i);
int ans = 0;
while (pq.size() >= 2) {
int u = pq.top(); pq.pop();
int t = pq.top(); pq.pop();
ans += u + t;
pq.emplace(u + t);
}
return ans;
}後序運算式(簡化版)
給你一個後序運算式,請求出這個式子的結果。
範例:6 3 / 1 4 - * 3 + 8 - 請輸出 11
(假設所有數字都小於等於九、且為正整數)
後序運算式(簡化版)
void sol(string s) {
vector<int> v;
for (auto i:s) {
if ('0' <= i && i <= '9') v.push_back(i - '0');
else {
int a = v.back(); v.pop_back();
int b = v.back(); v.pop_back();
if (i == '+') v.push_back(a + b);
if (i == '-') v.push_back(a - b);
if (i == '*') v.push_back(a * b);
if (i == '/') v.push_back(a / b);
}
}
return v.back();
}你講那麼快,
STL 的東西又那麼多,
我怎麼可能記得住?
中場休息
C++ reference
http://en.cppreference.com
中場休息
中場休息

中場休息

英文好難QAQ
中場休息

Disjoint Set 並查集
你知道你的爺爺的堂兄的父親不一定是
你隔壁同學的父親的表弟的祖父的哥哥的兒子嗎?
//disjoint set要自己手寫
//因為STL並沒有支援Disjoint 並查集
一開始每個人的老大都是自己

Disjoint 並查集
某一天 22 號打贏 1 號


Disjoint 並查集
但隔天 22 號卻輸給了7122


Disjoint 並查集
過了幾天,變成了這樣


find


int find(int x) {
if (x == boss[x]) return x;
else return find(boss[x]);
}unite

void unite(int x, int y) {
boss[y] = x;
}糟糕的情況...
此乃線性

好的情況
啟發式合併(其他技巧會教)

unite
void unite(int x, int y)
{
x = boss[x];
y = boss[y];
if (x == y) return;
if (size[x] < size[y]) swap(x, y);
boss[y] = x;
size[x] += y;
}
更好的情況
路徑壓縮


find
int find(int x) {
if (x == boss[x]) return x;
boss[x] = find(boss[x]);
return boss[x];
}
總結
int boss[N], size[N];
int find(int x) {
return x == boss[x] ? x : boss[x] = find(boss[x]);
}
bool same(int x, int y) {
return find(x) == find(y);
}
void unite(int x, int y) {
x = find(x), y = find(y);
if ( same(x, y) ) return;
if ( size[x] < size[y] ) swap(x, y);
boss[y] = x;
size[x] += size[y];
}例題演練
可愛的小動物
可愛的小動物
朋友的敵人是敵人
朋友的朋友是朋友
敵人的朋友是敵人
敵人的敵人是朋友
不能矛盾
可愛的小動物
如果只有朋友的話......

可愛的小動物
但是這個題目中有敵人...

可愛的小動物
如果只有朋友的話可以用並查集去維護
那敵人呢?


可愛的小動物
把朋友丟在黑色圈圈裡
把敵人丟到紫色圈圈裡


可愛的小動物
同個圈圈中的紫色和黑色是敵人
同個圈圈中的紫色和紫色是朋友
同個圈圈中的黑色和黑色是朋友
不同圈圈間沒什麼關係

可愛的小動物
紫色圈圈裡的數字可以當作 i + N

可愛的小動物
int aa = find(a), bb = find(b);
int ra = find(a + N), rb = find(b + N);
//a、b朋友
if (aa == rb) cout << "angry" << endl;
unite(a, b), unite(ra, rb);
// a、b敵人
if (aa == bb) cout << "angry" << endl;
unite(aa, rb), unite(bb, ra);
// a、b朋友嗎?
if (aa == bb) cout << "yeap" << endl;
else cout << "nope" << endl;
//a、b敵人嗎?
if (aa == rb) cout << "yeap" << endl;
else cout <<< "nope" << endl;Segment Tree 線段樹
一般人在資料結構都會在這關放棄
你,能夠撐過這一關嗎?
//Segment Tree要自己手寫
//因為STL並沒有支援
雖然也有人因此中毒
就是了......

Segment Tree 線段樹
將序列不斷切一半

Segment Tree 線段樹
儲存區間的答案

Segment Tree 線段樹
查詢區間的答案

Segment Tree 線段樹
查詢區間的答案
int seg[4 * N + 1];
int query(int id, int l, int r, int ql, int qr) {
if (qr < l || r < ql) return 0;
if (ql <= l && r <= qr) return seg[id];
int m = (l + r) / 2;
int v1 = query(id * 2, l, m, ql, qr);
int v2 = query(id * 2 + 1, m + 1, r, ql, qr);
return v1 + v2;
}Segment Tree 線段樹
修改單點的值

Segment Tree 線段樹
修改單點的值
void update(int id, int l, int r, int i, int v) {
if (l == r) {
seg[id] += v;
return;
}
int m = (l + r) / 2;
if (i <= m) update(id * 2, l, m, i, v);
else update(id * 2 + 1, m + 1, r, i, v);
seg[id] = seg[id * 2] + seg[id * 2 + 1];
}- 將區間的值都改成X
- 查詢區間的值
Segment Tree 線段樹
Segment Tree 線段樹
修改區間的值,懶惰標記

Segment Tree 線段樹
修改區間的值,懶惰標記
tag[4 * N + 1];Segment Tree 線段樹
路過就推一下,將懶惰標記向下推

Segment Tree 線段樹
將懶惰標記向下推
void push(int id, int l, int r) {
tag[id * 2] = tag[id];
tag[id * 2 + 1] = tag[id];
seg[id] = tag[id] * (r - l + 1);
tag[id] = 0;
}Segment Tree 線段樹
修改區間的答案
void update(int id, int l, int r, int ql, int qr, int x) {
push(id, l, r);
if (r < ql || qr < l) return;
if (ql <= l && r <= qr) {
tag[id] = x;
push(id, l, r);
return;
}
int m = (l + r) / 2;
update(id * 2, l, m, ql, qr, x);
update(id * 2 + 1, m + 1, r, ql, qr, x);
seg[id] = seg[id * 2] + seg[id * 2 + 1];
}Segment Tree 線段樹
查詢區間的答案
int query(int id, int l, int r, int ql, int qr) {
push(id, l, r);
if (qr < l || r < ql) return 0;
if (ql <= l && r <= qr) return seg[id];
int m = (l + r) / 2;
int v1 = query(id * 2, l, m, ql, qr);
int v2 = query(id * 2 + 1, m + 1, r, ql, qr);
return v1 + v2;
}如果是區間加X呢?
-
查詢區間加總
-
將區間所有數字加上一個數字X
區間加上X?
將懶惰標記向下推
void push(int id, int l, int r) {
tag[id * 2] += tag[id];
tag[id * 2 + 1] += tag[id];
seg[id] += tag[id] * (r - l + 1);
tag[id] = 0;
}區間加上X?
修改區間的答案
void update(int id, int l, int r, int ql, int qr, int x) {
push(id);
if (r < ql || qr < l) return;
if (ql <= l && r <= qr) {
tag[id] += x;
push(id);
return;
}
int m = (l + r) / 2;
update(id * 2, l, m, ql, qr, x);
update(id * 2 + 1, m + 1, r, ql, qr, x);
seg[id] = seg[id * 2] + seg[id * 2 + 1];
}例題演練
RMQ
RMQ
給你一個數列,請動態支援以下兩種操作
-
詢問區間最大值
-
將一個區間所有數字修改成V
RMQ push
void push(int id) {
tag[id * 2] = tag[id];
tag[id * 2 + 1] = tag[id];
seg[id] = tag[id];
tag[id] = 0;
}RMQ query
int query(int id, int l, int r, int ql, int qr) {
push(id);
if (qr < l || r < ql) return 0;
if (ql <= l && r <= qr) return seg[id];
int m = (l + r) / 2;
int v1 = query(id * 2, l, m, ql, qr);
int v2 = query(id * 2 + 1, m + 1, r, ql, qr);
return max(v1, v2);
}RMQ update
void update(int id, int l, int r, int ql, int qr, int x) {
push(id);
if (r < ql || qr < l) return;
if (ql <= l && r <= qr) {
tag[id] = x;
push(id);
return;
}
int m = (l + r) / 2;
update(id * 2, l, m, ql, qr, x);
update(id * 2 + 1, m + 1, r, ql, qr, x);
seg[id] = max( seg[id * 2], seg[id * 2 + 1] );
}BIT樹狀樹組
你說你不想寫線段樹?那就寫BIT吧
//Binary Indexed Tree要自己手寫
//因為STL並沒有支援二進位制
1:0001
2:0010
3:0011
4:0100
5:0101
6:0110
7:0111
8:1000
BIT 樹狀樹組
樹狀樹組的長相

BIT 樹狀樹組
樹狀樹組查詢

BIT 樹狀樹組
樹狀樹組修改

舉個例子

2,5,1,4,7,10,2,6
單點加值

2,12,1,4,7,10,2,6
單點加值
update
int bit[N];
void update(int i, int x) {
while (i <= N) {
bit[i] += x;
i += i & -i;
}
}Lowit Bit
1:0001:0001
2:0010:0010
3:0011:0001
4:0100:0100
5:0101:0001
6:0110:0010
7:0111:0001
8:1000:1000
二進位制,最小的一個 1-bit 的值
區間(前綴)查詢
query

2,12,1,4,7,10,2,6
區間(前綴)查詢
query
int query(int i) {
int res = 0;
while (i > 0) {
res += bit[i];
i -= i & -i;
}
return res;
}區間查詢
將兩個前綴相減
如何單點改值?
把第二格改成12

2,5,1,4,7,10,2,6
如何單點改值?
等價於把第二格加上7

2,12,1,4,7,10,2,6
例題演練
逆序數對
逆序數對
給定一個數列,求有多少個(i, j)數對滿足:
逆序數對
檢查這個元素前面有多少元素在

逆序數對
檢查這個元素前面有多少元素在
//cnt[i]紀錄1~i總共出現在s中幾次
for(auto i:s) {
ans += cnt[i - 1] - query(i - 1);
update(i, 1);
}
cout << ans << '\n';逆序數對
小提醒:
-
有可能會有重複的元素
-
有可能會超出int範圍
-
有可能有負數
額外延伸

二維BIT修改
int r, c, bit[N][N];
void update(int x, int y, int v) {
while (x <= r) {
int t = y;
while (y <= c) {
bit[x][y] += v;
y += y & -y;
}
y = t;
x += x & -x;
}
}二維BIT查詢
int query(int x, int y) {
int res = 0;
while (x > 0) {
int t = y;
while (y > 0) {
res += bit[x][y];
y -= y & -y;
}
y = t;
x -= x & -x;
}
return res;
}Sparse Table稀疏表
你說你不想寫BIT,那就寫Sparse Table吧
前提是對面不會動
//Sparse Table要自己手寫
//因為STL並沒有支援Sparse Table 稀疏表
稀疏表的示意圖

Sparse Table 稀疏表
稀疏表的查詢

Sparse Table 稀疏表
int n, s[LOGN][N];
for(int j = 1; (1 << j) <= n; j++)
for(int i = 0; i + (1 << j) <= n; i++)
s[j][i] = min(
s[j - 1][i],
s[j - 1][i + (1 << (j - 1) )]
);
int query(int l,int r) {
int k = __lg(r - l + 1);
return max( s[k][l], s[k][r - (1 << k) + 1] );
}例題演練
桑京邀請賽
桑京邀請賽
給定一個序列長度為N,並有許多詢問
每次詢問一個區間中的最大值與最小值。
(空間只夠開 10N 個 int)
桑京邀請賽
對稀疏表做滾動




int s1[2500000], n, m;
int l[1000006], r[1000006];
bitset<1000006> ok;
int main(){
scanf("%d %d",&n, &m);
for(int i = 0 ; i < m ; i++)
scanf("%d %d", &l[i], &r[i]), l[i]--, --r[i];
for(int i = 0 ; i < n ; i++) scanf("%d", &s1[i]);
for(int i = 0 ; i < m ; i++)
if( lo(l[i], r[i]) == 0 )
l[i] = s1[ l[i] ], ok[i] = 1;
for(int i = 1; (1 << i) <= n ; i++){
for(int j = 0 ; j + (1 << i) <= n ; j++)
s1[j] = max( s1[j], s1[ j + ( 1 << (i - 1) ) ] );
for(int j = 0 ; j < m ; j++)
if( lo( l[j], r[j] ) == i && !ok[j] )
l[j] = max(
s1[ l[j] ],
s1[ r[j] - ( 1 << i ) + 1 ] ), ok[j] = 1;
}
for(int i = 0 ; i < m ; i++) printf("%d\n", l[i]);
}非STL例題演練
如果沒時間的話就請請大家回去練習吧
食物鏈
有三種動物A, B, C,A會吃B、B會吃C、C會吃A
現在有N隻動物,分別屬於ABC其中一種,並有人給你許多它們之間的關係
- X與Y是同類
- X吃Y
但這個人有可能說謊,只要滿足以下就算這個人說謊
- X或Y大於N
- X吃X
請問這個人說了多少謊?
食物鏈
可愛小動物的三種關係的版本
詳細實作就請大家參考可愛小動物那一題
地雷區
在一張N X M的棋盤上,有些地方有地雷,一個地雷被引爆的話會炸到周圍3 X 3的區域;若兩個地雷的3 X 3區域有重疊的話,引爆其中一個的話另一個也會被連鎖引爆。
請問要人工引爆多少個地雷才能炸掉所有地雷?
地雷區

from TIOJ
地雷區
for (int t = 0; t < v.size(); ++t)
{
pair<int,int> i = v[t];
for(int dx = -2; dx <= 2; ++dx) for(int dy = -2; dy <= 2; ++dy)
{
int x = i.first + dx, y = i.second + dy;
int d = lower_bound(all(v), make_pair(x,y)) - v.begin();
if(v[d] != make_pair(x,y)) continue;
d = find(d);
int dd = find(t);
if(dd != d) fa[d] = dd, ++cnt;
}
}
cout << n - cnt << endl;強者物質
有一個長度最多為 10^5 的數列,有正有負。
請問有多少區間的和為正的?
並請問區間和最大為多少?
第二個問題的答案是DP,就當作早上的課的練習請大家想想囉
強者物質
看到區間和,有個小技巧是換成前綴和來思考
區間和為正
等價於前綴和相減為正
等價於後面的前綴和比前面的前綴和大
計算多少個後面的元素比前面的大可以用BIT
強者物質
for (int i = 1; i <= n; ++i) {
ans += (s[i] > 0);
\\離散化
s[i] = lower_bound(all(li), s[i]) - li.begin() + 1;
add(s[i], 1);
ans += sum(s[i] - 1);
}
cout << ans << endl;隕石
有一個無限長的數列,一開始所有數字都是零。
給 N 個區間 (L, R),要將那個區間的數字都加一。
現在你能刪掉 K 個區間,請問最後整個數列中的最大值最小可以是多少?
隕石
二分搜答案
將區間放在數線上,如果數線上有個點被太多線段覆蓋到的話,代表就要刪掉線段
要刪掉線段的話,刪掉右界最右邊的最好
隕石
pair<int,int> a[N];
set<int> s;
bool check(int mid)
{
int res = k;
s.clear(); // 儲存線段在數線上的資訊
for (int i = 0; i < n; ++i) {
while(s.size() && *s.begin() <= a[i].first)
s.erase(s.begin()); // 過期了
s.insert(a[i].second);
while(s.size() > mid) { //現在這個點被太多線段覆蓋
s.erase((--s.end())); // 刪掉右界最右邊的
--res; //我只能刪掉K個區間
}
if(res < 0) return false;
}
return true;
}矩形覆蓋面積計算
給定座標平面上的許多平行於 X 軸與 Y 軸的矩形
請求出所有矩形的面積的聯集大小
矩形覆蓋面積計算
掃描線

矩形覆蓋面積計算
計算此時的 y 軸上有多少個被覆蓋

矩形覆蓋面積計算
宣告
int flag[4 * N + 1];
int count[4 * N + 1];矩形覆蓋面積計算
pull
void pull(int id, int l, int r) {
if ( flag[id] > 0 )
count[id] = r - l + 1;
else count[id] =
count[id * 2] + count[id * 2 + 1];
}矩形覆蓋面積計算
Query
int query() {
return count[1];
}矩形覆蓋面積計算
Update
void update(int id, int l, int r, int ql, int qr, int v) {
if (qr < l || r < ql) return;
if (ql <= l && r <= qr) {
flag[id] += v;
}
else {
int m = (l + r) / 2;
update(id * 2, l, m, ql, qr, v);
update(id * 2 + 1, m + 1, r, ql, qr, v);
}
pull(id, l, r);
}如果,生命的腳印終有一天
會被時間的塵埃掩埋... ...
那我們就永遠不能
--停下腳步
FIN
Copy of 2020IONCamp基礎資結
By tunchin kao
Copy of 2020IONCamp基礎資結
- 73