寫在前面
大概是筆記吧
部分內容引用了\(\texttt{OI-Wiki}\)和【AgOHの資料結構】主席樹
部分圖片出處:【AgOHの資料結構】主席樹
基本區間操作
-
單點修改,區間詢問:對於每個線段樹上的區間維護一個區間的資訊之和
\(eg.\)單點修改,詢問區間和,區間最大值
-
區間修改,單點詢問:每次修改時對區間打上一個修改表及,在詢問和修改時,一旦訪問到一個區間,就將他的標記下傳
\(eg.\)區間加,求單點的值
例題
線段樹與標記下傳
區間修改區間詢問的線段樹實現的重點:
- 標記之間的合併
- 區間資訊之間的合併
- 區間資訊與標記的合併
只要能快速地進行以上三個操作,理論上標記和區間資訊可以是任何東西(劃重點!),甚至是平衡樹
權值線段樹
權值線段樹就是對一個值域上值的個數進行維護的線段樹
舉個例子,對於\(1,2,2,3,4,4,5,5,6,7,8,8\),他的權值線段樹就是
其中中括號內是值域,圓圈內表示的是值在對應值域裡的數的個數
線段樹合併
線段樹合併其實就是動態開點權值線段樹合併
動態開點: 就是用哪個地方就開哪個地方,每次訪問之前看看是否為空,為空就新建一個節點。詢問時如果遇到沒有開過點的點,就直接返回即可,時間複雜度是\(\log n\)的
普通的靜態線段樹左兒子為\(rt<<1\),右兒子為\(rt<<1|1\),用的空間大概是\(4n\),動態開點線段樹中左右兒子是不定的,要根據實際情況判斷
為什麼要用動態開點呢?因為有些時候是不能用靜態線段樹的,比如說:
-
值域很大
比如\(10^9\),然而真正有效的點其實是很少的
(當然,離散化牛逼
-
可持久化
有\(n\)個線段樹\(T_1,T_2...T_n\),如果靜態開空間當然是開不下的,但是如果它和主席樹一樣,\(T_i\)是在\(T_{i-1}\)的基礎上進行修改的,就可以用可持久化的方法進行動態開點
兩個可重集合合併的方法
假設有兩個集合\(S,T\),假設為了維護它們的資訊,已經建好了兩棵線段樹\(A_S,A_T\),現在要合併這兩個區間,應該如何合併呢?
-
啟發式合併
如果\(|S|<|T|\),那麼就列舉\(S\)中的元素,將其加到\(T\)的線段樹中
小的合併到大的上,複雜度\(O(n\log^2n)\),比較低效
-
線段樹合併
根據線段樹 美妙的結構:如果下標相同,那麼兩棵線段樹的結構就會完全相同
設當前合併的節點為\(X,Y\),區間為\([L,R]\),用\(merge(X,Y,L,R)\)實現合併,考慮如何實現
-
如果\(X\)為空,則返回\(Y\)
if (X == NULL) return Y;
-
如果\(Y\)為空,則返回\(X\)
if (Y == NULL) return X;
-
如果\(L=R\),即為葉節點,直接將兩點\(sum\)相加
if (L == R) { int z = newnode(); sum[z] = sum[X] + sum[Y]; return z; //合併的結果是z }
-
否則新建節點\(z\),分左右子樹合併
int z = newnode(), mid = L + R >> 1; lson[z] = merge(lson[X], lson[Y], L, mid);//左子樹 rson[z] = merge(rson[X], rson[Y], mid + 1, R);//右子樹 update(z);//從子樹中獲得資訊 return z;
以上就是線段樹合併的實現和虛擬碼。複雜度為\(O(n\log n)\),證明:
以上的執行過程中,其實只有兩種情況
-
\(X,Y\)中有一個為\(NULL\)
直接返回,複雜度\(O(1)\)
-
\(X,Y\)都不為\(NULL\)
單次執行過程複雜度為\(O(1)\)(只是單次)
單次執行的過程中新建了一個節點,去掉了兩個節點,總共相當於去掉了一個節點,所以執行總次數就是\(線段樹點個數O(\text{線段樹點個數})\)級別的
一開始線段樹中的節點數是\(O(n\log n)\)級別的,所以均攤下來總複雜度就是\(O(n\log n)\)級別
-
可持久化線段樹
原理
關於 可持久化資料結構 的介紹,去看OI-Wiki
可持久化線段樹是一種可持久化的資料結構,其特點是:在更新時,可持久化線段樹儘可能與之前某一個版本共用一部分結點,從而起到節省空間的作用。
如下圖,現在有一顆有7個節點的線段樹
我們想要對圖中的紅點進行單點修改(其實就是單點修改,紅點是包含它的區間)
樸素的做法是建一棵新的線段樹,但是這樣十分浪費空間,因為線段樹一次修改只會修改\(\log\)個節點,為了修改這\(\log\)個節點而去重新建一棵線段樹是非常不可取的。
因此我們可以只去新建這\(\log\)個節點,並將新建的節點連線到原線段樹中要修改的點連線的位置上,如下圖
這樣就大大節省了空間。
實現
見此題洛谷 P3919 【模板】可持久化線段樹 1(可持久化陣列)
/*
Author:Loceaner
*/
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define lson t[x].l
#define rson t[x].r
#define lc t[pre].l
#define rc t[pre].r
using namespace std;
const int A = 1e6 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
inline int read() {
char c = getchar(); int x = 0, f = 1;
for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
return x * f;
}
int n, m, a[A], cnt, num, rt[A];
struct tree { int l, r, w; } t[A * 20];
void build(int &x, int l, int r) {
x = ++num;
if (l == r) { t[x].w = a[l]; return; }
int mid = (l +r) >> 1;
build(lson, l, mid), build(rson, mid + 1, r);
}
void update(int &x, int pre, int l, int r, int pos, int val) {
x = ++num, lson = lc, rson = rc;
if (l == r) { t[x].w = val; return; }
int mid = (l + r) >> 1;
if (pos <= mid) update(lson, lc, l, mid, pos, val);
else update(rson, rc, mid + 1, r, pos, val);
}
int query(int x, int l, int r, int k) {
if (l == r) return t[x].w;
int mid = (l + r) >> 1;
if (k <= mid) return query(lson, l, mid, k);
return query(rson, mid + 1, r, k);
}
int main() {
n = read(), m = read();
for (int i = 1; i <= n; i++) a[i] = read();
build(rt[0], 1, n);
while (m--) {
int k = read(), opt = read(), pos, val, x;
if (opt == 1) pos = read(), val = read(), update(rt[++cnt], rt[k], 1, n, pos, val);
else if (opt == 2) x = read(), rt[++cnt] = rt[k], cout << query(rt[cnt], 1, n, x) << '\n';
}
return 0;
}
例題集合
例題1
給定序列\(a[1...n]\),\(Q\)次詢問,要求支援:
- 區間乘
- 求區間最大欄位和
\(n,Q\le 10^5\)
由上面重點中所說,我們最重要的就是解決以下三個問題
- tag+tag
- 區間+區間
- 區間+tag
tag+tag
容易想到\(tag\)標記的就是區間是否乘\(-1\)
維護一個\(bool\)值,表示是否乘\(-1\),如果乘了偶數次就相當於沒乘
區間+區間
要維護區間的最大子段和\(ans\),不僅要維護值,還要維護左右端點
合併兩個子區間時,假設左子區間為\(L\),右子區間為\(R\),那麼最大子段和的合併有以下三種情況:
-
\(L\)的最大子段和
-
\(R\)的最大子段和
-
跨越\(L\)和\(R\)的最大子段和
對於這種情況可以分成兩部分,一個是\(L\)的字尾,另一個是\(R\)的字首,因此還要維護區間的最大字尾和\(suf\)和最大字首和\(pre\)
答案就是\(\max\{L_{ans},R_{ans},L_{suf}+R_{pre}\}\)
考慮如何更新最大字尾和和最大字首和,以最大字尾和為例,兩個子區間合併時,最大字尾和有兩種情況:
- 右子區間的最大字尾和\(R_{suf}\)
- 右子區間的和\(R_{sum}\)+左子區間的最大字尾和\(L_{suf}\)
因此我們還需要維護區間的和\(sum\)
合併時最大字首和同理,有 左子區間的最大字首和\(L_{pre}\) 和 左子區間的和\(L_{sum}\)+右子區間的最大字首和\(R_{pre}\) 兩種情況
綜上我們要維護\(sum,ans,pre,suf\)四個值以及端點
區間+tag
當區間乘了\(-1\)時,\(sum\)可以直接變為相反數,但是其他三個值並不能直接這麼幹
考慮區間乘\(-1\)時,最大欄位和的變化
-
最大子段和 會變成 負的最小欄位和
-
負的最小欄位和 會變成 最大子段和
因此我們在維護最大欄位和、最大字首、最大字尾時,要維護同一套的最小欄位和、最小字首、最小字尾,在乘\(-1\)時,直接將兩者交換,再乘\(-1\)即可
還有一些細節的地方,在此不再贅述(因為我也不會寫也沒有提交的地方(手動滑稽……然後這道題就做完了
例題2
給定序列\(a[1...n]\),支援單點修改,每次求區間單調棧大小
\(n,Q\le 10^5\)
區間單調棧是什麼呢?對於一個區間,建立一個棧,首先將第一個元素入棧,從左往右掃,如果當前元素大於等於棧頂元素,就將其入棧,由此形成的棧即為單調不減的區間單調棧。
轉化一下,其實就是求區間內滿足\(a[i]=\max\limits_{j=1}^ia[j]\)的\(a[i]\)的個數。
一個自然的想法是維護單調棧的大小\(siz\),那麼如何去進行區間的合併呢?
合併兩個子區間時,假設左子區間為\(L\),右子區間為\(R\),考慮合併之後的單調棧的組成部分:
-
第一部分:\(L\)的單調棧
因為單調棧是從左往右做的,所以\(L\)的單調棧必然是大區間單調棧的一部分
-
剩餘部分
設出函式\(calc(now,pre)\),\(now\)表示當前節點,\(pre\)表示當前單調棧的棧頂,\(calc\)函式計算剩餘部分的單調棧的大小
總的單調棧大小\(siz\)就是\(L_{siz}+calc(R,L_{max})\)
calc的實現
現在有\(calc(now,pre)\),\(l\)表示\(now\)的左子樹,\(r\)表示\(now\)的右子樹
- 如果\(pre>l_{max}\),說明整個左子區間都不用考慮了,此時答案就變成了\(calc(r,pre)\)
- 如果\(pre\le l_{max}\),此時\(l\)是有貢獻的,他對\(siz\)的貢獻就是\(calc(l,pre)\),右子樹的貢獻為\(calc(r,l_{max})\),總貢獻就是\(calc(l,pre)+calc(r,l_{max})\)
至此\(calc\)就推完了,但是我們發現如果僅僅是這樣的話,在最壞的情況下,複雜度會爆炸,那麼怎麼優化呢?
觀察\(calc(r,l_{max})\),發現它就等於\(siz-l_{siz}\),所以第二種情況就可以變成\(calc(l,pre)+siz-l_{siz}\),其中\(siz\)都是可以處理好的
這樣我們就可以在\(O(\log n)\)的時間裡完成一次合併
總時間複雜度\(O(Q\log^2 n)\)
例題3
有一個序列\(a[1...n],\)每個\(a[i]\)是\((c,x,y)\),表示顏色和座標
現在支援單點修改以及區間詢問:顏色不同的曼哈頓距離最大的一對點的距離
\(n,Q\leq10^5\)
這個題其實就是一堆普通的處理的小技巧疊加起來
曼哈頓距離:設座標系中兩點的座標為\((x_1,y_1)\)、\((x_2,y_2)\),則\(|x_1-x_2|+|y_1-y_2|\)就是兩點之間的曼哈頓距離
簡單的小技巧就是把它拆成四個部分,如下
顯然這四種的最大值就是曼哈頓距離,所以分情況討論,討論每種情況下顏色不同的曼哈頓距離最大的一對點的距離,最後取最大值,就是我們要的答案了
以第一種為例,其實可以寫成\((x_1+y_1)-(x_2+y_2)\),這就相當於每個點有了一個權值\(w=x+y\),那麼現在的問題就相當於找權值最大和權值最小的點,而且他們的顏色要不同
如果不考慮顏色不同,我們就可以用線段樹直接維護區間權值的最大值和最小值
那麼如果考慮顏色不同該怎麼做呢?一種不太顯然的做法是:在維護最大值和其顏色的同時,維護一個次大值以及其顏色,保證最大值和次大值的顏色不同,在維護最小值和其顏色的同時,維護一個次小值以及其顏色,保證最小值和次小值的顏色不同
當區間最大值和最小值顏色不同時,答案就是兩者之差,當兩者顏色相同時,有 最大值-次小值 和 次大值-最小值 兩種情況,兩者顏色一定不同,所以取\(\max\)即可
顏色不同的次權值如何維護?
假設現在已經知道了左子區間的最大值、次大值及其顏色,右子區間的最大值、次大值及其顏色,那麼新區間的最大值一定為兩個子區間種的最大值之一,次大值的話其他三個值都有可能,選出其中最大的且與最大值顏色不同的一個即可
其他三種情況也可以類似的處理
這樣的話這個題就做完了,直接開四棵線段樹就好
技巧總結
- 對曼哈頓距離的處理
- 對顏色不同的處理
例題4
給定\(a[1...n]\),要求支援單點修改,以及區間詢問\(a[l...r]\)不能組成的最小的數
\(n,Q\le2\times10^5,1\le a[i]\le10^9\)
時限\(15s\)
聽老師說十五秒直接三個\(log\)就能艹過了
首先一定要有一個值為\(1\)的數,否則最小的不能組成的數就是\(1\)
小結論
假設現在可以表示\(1\sim x\)中的所有數,這個時候如果來了一個值為\(x+1\)的數,那麼我們就可以表示出\(1\sim 2x+1\)
這是比較顯然的,因為\(1\sim x\)都可以表示出來,\(x+1\)有了,之後的\(x+2\sim 2x+1\)就可以直接用\(x+1\)和之前的數拼起來了
推廣一下,如果現在已經可以表示出\(1\sim x\)中的所有數,如果來了一個值為\(y\)的數,且\(y\leq x+1\),那麼我們就可以拼出\(1\sim x+y\)中的數
假設\(T\)表示能表示出\(1\sim T\),那麼只要加入一個\(\leq T+1\)的數,\(T\)的範圍就會大大增加,所以就可以擴充套件到區間裡\(\le T+1\)的數和
要用有鼠撞樹卒的主席樹維護對於一個二維區間裡\(\leq T+1\)的數的和
我不會了,爬
例題5
給定\(a[1…n]\)要求支援:
- 區間加
- 區間變成\(max(a[i]+v,0)\),\(v\)可以是正數也可以是負數
- 求單點當前值
- 求單點歷史最大值
- 區間覆蓋
\(n,Q\le10^5\)
他 跳 了
例題6
給定序列列\(a[1…n]\),要求支援區間和以及讓\(a[L…R]\)對\(x\)取\(\min\)
\(n,Q\le10^5\)
他 跳 了
例題7
給定序列\(a[1...n]\)以及\(k,d\),求一個最長的區間,使得最多加入\(k\)個數之後,排序得到的是一個公差為\(d\)的等差數列
\(n\le2\times10^5\)
要素察覺:必須要是一個公差為\(d\)的等差數列(滑稽
那麼:
- 這個等差數列裡的所有數$ \bmod d$的結果應該一樣
- 區間內沒有重複的數
考慮怎麼進行
-
首先將序列分成若干個\(x \bmod d\)都一樣的子區間
在從左往右掃的過程中,如果遇到了與前面\(x\bmod d\)的值不同的數,就將左邊\(x\bmod d\)值相同的數作為一個獨立的區間來處理,最後就可以分成若干個\(x \bmod d\)都一樣的子區間
-
對於一個滿足\(x\bmod d=c\)的數列,把所有的\(x\)變成\(\dfrac{x-c}{d}\),這樣整個序列的公差就為\(1\)了,問題就轉化成了加入\(k\)個數,使區間\(sort\)後公差為\(1\)。(歸一化)
-
對於一個區間\([L,R]\),考慮如何算出最少加幾個數
- 首先不能有重複
- 顯然最少加的數的個數就是\(\max(L,R)-\min(L,R)+1-(R-L+1)\)
-
從小到大列舉\(R\),相當於求最小的\(L\),使得
-
\([L,R]\)無重複
從小到大列舉\(R\),對於新的\(a[R]\)很容易知道(比如用\(set\)維護)它前面一個和他相等的數\(a[T]\),那麼\(L\)至少要大於\(T\)
-
\(\max(L,R)-\min(L,R)+1-(R-L+1)\le k\)
即\(\max(L,R)-\min(L,R)+L\le k + R)\)
用線段樹維護\(w[L]=\max(L,R)-\min(L,R)+L\)
假如\(L\)的下界是\(T\),那麼我們要在\([T+1,R]\)中找最左的位置使得\(w\le k + R\)
-
如何維護\(w\)?用單調棧。維護一個單調遞減的棧,因為單調棧遞減的性質,所以當一個大於棧頂的元素加入時,會不斷地彈出棧頂,直到棧頂元素大於此元素為止,再將此元素入棧。
此處,單調棧可以將\(\max(L,R)\)分成遞減的若干段,考慮如何實現:
如圖所示,假設有一個單調遞減的單調棧,其中\(S1\ge S2\ge S3\),\(S3\)為棧頂元素
由於單調棧的性質,\(S1\)和棧中上一個元素之間可能是有別的元素的,所以\(S1,S2,S3\)其實是代表了三個區間的最大值
此時,單調棧中來了一個新的元素\(a\),顯然\(a>S1>S2>S3\),所以我們要將\(S1,S2,S3\)彈棧
在彈棧時,因為此時這三個元素的值不能代表這三個綠色段的最大值了,所以我們需要將三個段的貢獻從線段樹中減去
同時,新的元素\(a\)就加進來了,這個時候就可以發現原來三個段的代表元素被彈出之後,其實就被新元素所接管了,所以再對一整個區間進行區間加操作即可
這樣單調棧就實現了將\(\max(L,R)\)分成了遞減的若干段,由此就可以不斷進行段樹的區間加、區間減。
\(\min\)的維護同理。
最後再呼叫一下找最左邊的\(w\le k+R\)的演算法就好了。(啊這……不講了不講了,自己\(yy\),可以從一道題目入手)
總時間複雜度\(O(n\log n)\)
原題:CF407E
例題8
給定序列\(a[1...n]\),\(Q\)次詢問\(a[L...R]\)中\(,,,,,,,,,,,,,,,L\le i\le R,L\le j\le R\)且\(i\ne j\)時\(|a_i-a_j|\) 的最小值
\(n,Q\le2\times10^5\)
維護區間的一種比較通用的處理方法:
從小到大列舉\(R\),維護一顆線段樹,\(w\)的下標\(L\)表示\(L\sim R\)的答案,每次\(R+1\)時做一些修改
好處:每次只需考慮新的數
假設現在有一個\(a\)序列,列舉右端點從\(R-1\)轉移到了\(R\),設尋找的一個數為\(x\)
那麼可以從兩個方面考慮:
-
\(x>a[R]\)
從\(R\)開始向左尋找比\(a[R]\)大的最近的數\(a[x]\),由此可以得出\(L=1\sim x\)有一個新的備選的值:\(a[x]-a[R]\),取\(\min\)即可
但這是不夠的,我們需要繼續往左尋找新的數,假設為\(a[y]\)
那麼\(a[y]\)首先必須滿足的條件就是\(a[R]\le a[y]\le a[x]\),否則一定不會更新答案,此時他就會造成一個\(a[y]-a[R]\)的影響,同時還有一個影響就是\(a[x]-a[y]\),這個影響其實在右端點列舉到\(x\)時就已經統計過了,所以如果\(a[y]-a[R]\)已經比\(a[x]-a[y]\)大的話,就沒有更新的必要了,所以\(a[y]-a[R]<a[x]-a[y]\)
所以\(y\)滿足的條件為\(y< x,a[y]\in[a[R],\dfrac{a[R]+a[x]}{2}]\),且\(y\)最大
這個東西可以用主席樹維護:線段樹用權值線段樹,維護一下在這個權值區間裡下標最大的數,查詢時查詢一下主席樹在\(x\)時的歷史資訊。
找到\(y\)之後,就用\(a[y]-a[R]\)去更新\(w[1...y]\)
之後再找\(z\),同上要滿足的條件是:\(a[z]-a[R]<a[y]-a[z]\)
所以\(a[z]\in[a[R],\dfrac{a[R]+a[y]}{2}]\)
之後一次次尋找,最多找\(\log\)次
-
\(x<a[R]\)
做法同上。
做……完……了……
原題:CF765F
例題9
給定\(n\),定義\(work(x,y)\)等於\([1,n]\)的線段樹上對\([x,y]\)進行區間詢問後訪問到的點的個數
給定\(L,R\),求\(work(i,j)\)的和,其中\(L\le i\le j\le R\)
\(n,Q\le 10^5\)
考慮\(work(x,y)\)如何快速求
對於線段樹上一個點\([L,R]\),被訪問到的條件有:
- 與\([x,y]\)有交
- \([x,y]\)不包含\([fa_L,fa_R]\)(\(fa\)表示父親節點)
假設現在的詢問是\([X,Y]\),那麼要求的就是
-
如果\(X,Y\)包含了父區間
設\(C(len)=\dfrac{len\times(len+1)}{2}\)
那麼首先要去掉的就是與\(L,R\)不交的區間:\(C(L-X)+C(Y-R)\)
第二步取出的是雖然與有交,但是包含了父親區間即:\((fa_L-X+1)(Y-fa_R+1)\)
最後答案是\(C(Y-X+1)-[C(L-X)+C(Y-R)]-(fa_L-X+1)(Y-fa_R+1)\)
-
如圖所示
這樣的情況就是\(C(Y-X+1)-C(Y-R)\)
-
其他同理,分析一下即可
這樣的複雜度是\(O(nQ)\)的,如何優化呢?
\(C(Y-X+1)-[C(L-X)+C(Y-R)]-(fa_L-X+1)(Y-fa_R+1)\)展開其實就是一個二次函式,是可以預處理的
聽不懂了
爬。。有空再補
此題總結
- \(work(x,y)\)
- \(O(nQ)\)
- 對\(O(nQ)\)剪枝,卡常數
- 某些情況整個子樹可一起算
例題10
有\(Q\)次操作,每次給定\(L,R,K,B\):對於$L<=i<=R \(令\)a[i]=\max(a[i],K\times i+B)\(,單點詢問\)a[i]$
\(n,Q\le10^5\)
顯然\(K\times i + B\)是一條線段,所以每次我們要加一條線段進去,就像下面這樣
一個想法是把線段視為一個tag,打到線段樹上去,但是顯然是不能這麼做的,因為線段在某些情況下無法合併(或很難合併),如下圖
其實這題就是李超線段樹模板了
\(addline(rt,line)\)表示往\(rt\)上插一條線段\(line\),分類討論一下,無論如何進行,最後都會變成\(addline(child,line)\)的形式,所以給一個區間加一條線段的最壞複雜度為\(O(\log n)\),又因為是對線段樹的區間加直線,所以會給\(\log\)個區間進行\(addline\)操作,所以每次修改的總複雜度就是\(O(\log^2 n)\)
實現時需要記錄線段左端點、右端點和區間\(mid\)時線段的值,然後根據原來線段這三個點的大小和現在的線段這三個點的大小進行比較。
例題11
定義\(Min-Max\)樹是一顆二叉樹,每個葉子節點有一個權值
現在定義每個非葉子的權值為:有\(p\)的概率是兩個兒子的權值的\(max\),有\(1-p\)的概率是兩個兒子的權值的\(min\)
對於所有可能的\(i\),輸出根節點權值為\(i\)的概率
\(n\le5\times10^5\)
例題12
給定一棵\(n\)個點的樹,點有顏色,每次詢問點\(x\)子樹內距離不超過\(d\)的點有多少種不不同的顏色
\(n\le10^5\)
沒人讓我更新/kk,那我就咕
例題13
給定陣列 \(a[1…n]\),一開始都為\(1\),要求支援以下三種操作:
給定\(L,R\),對於\(L\le i\le R\),令\(a[i]=\varphi(a[i])\)
給定\(L,R,x\),對於\(L\le i\le R\),令\(a[i]=x\)
求區間和
沒人讓我更新/kk,那我就咕
例題14
給定\(n\)個區間,以及每個點的價值\(val[1…M]\),對於每個區間可以選擇里面的一個點\(i\),獲得價值\(val[i]\),每個點最多隻能被選一次,求最大價值
• \(n,M\le5000\)
沒人讓我更新/kk,那我就咕
例題15
有\(n\)個點,需要支援兩種操作:
新增一條邊,保證加完後還是森林
給定\((u,v)\),保證\((u,v)\)是一條存在的邊,求有多少點對\((x,y)\)經過了了邊\((u,v)\)
沒人讓我更新/kk,那我就咕
例題16
給定一個陣列\(a[1…n]\),首先有\(Q\)次操作,每次會將一個區間升序排序或者降序排序
求操作後\(a[K]\)的值
沒人讓我更新/kk,那我就咕
例題17
沒人讓我更新/kk,那我就咕