做題 cf603E 線段樹分治

2022-05-13 13:46:02 字數 1786 閱讀 5900

首先感謝題解小哥,他在標算外又總結了三種做法。

此處僅提及最後一種做法。

首先考慮題目中要求的所有結點度數為奇數的限制。

對於每乙個聯通塊,因為所有結點總度數是偶數,所以總結點數也必須是偶數的。即所有聯通塊都要是偶數大小。

而考慮任意乙個偶數大小的聯通塊,我們任意取它的乙個生成樹,然後進行如下演算法:

設 1 為根結點;

按深度從大到小列舉每乙個結點

若其當前度數為偶數

則斷開與他的父結點的連邊;

這樣除根結點外的所有結點的度數都能保證為奇數,而因為總度數和為偶數,所以根結點的度數也為奇數。

因此,我們得到

存在方案使得所有結點度數為奇數 \(\iff\) 所有聯通快大小為偶數。

注意到偶數加偶數還是偶數,換言之,新增多餘的邊是不會使答案變劣的。並且,答案是單調遞減的。所以我們可以達到如下結論:

如果第\(j\)次詢問的答案大於等於第\(i\)條邊的邊權,那麼可以在處理詢問區間\(\left[ i,j-1 \right]\)時直接將第\(i\)條邊加上。

這樣我們就可以用線段樹分治。我們對詢問開線段樹,從後往前處理。遍歷到葉結點時按邊權暴力從小到大列舉邊(在上一次基礎上),與此同時確定了列舉到的邊產生貢獻的範圍,用線段樹實現區間修改。在遍歷時需要維護支援撤銷操作的並查集。這相當於是在分治的同時確定每條邊的刪除時間,即答案小於它的邊權的時刻。

時間複雜度\(o(nlog^2n)\)。

#include using namespace std;

const int n = 300010;

int odd;

struct record

} rec[n * 10];

int uni[n],sz[n],cnt;

int get_fa(int x)

void unio(int x,int y) ;

odd -= tmp;

rec[++cnt] = (record) ;

uni[x] = y;

rec[++cnt] = (record) ;

sz[y] += sz[x];

}struct data

} dat[n];

vectoredg[n << 2];

int n,m,cur,ans[n];

void modify(int lp,int rp,int id,int x,int l,int r)

void solve(int x,int l,int r) else

if (odd > 0) ans[l] = -1;

else ans[l] = dat[cur-1].v;

} while (cnt > tmp)

rec[cnt--].rollback();

}int main() ;

} for (int i = 1 ; i <= n ; ++ i)

uni[i] = i, sz[i] = 1;

sort(dat+1,dat+m+1);

cur = 1;

solve(1,1,m);

for (int i = 1 ; i <= m ; ++ i)

printf("%d\n",ans[i]);

return 0;

}

小結:其實我根本不會想到糊結論……線段樹分治的做法,相比lct做法更加巧妙,利用題目的特殊性質從而簡化了**量。

CF671E(線段樹 單調棧)

傳送門 神仙題,看題解看了乙個多小時才看懂 首先我們設 pre i 和 suf i 分別表示 1 到 i 需要的額外油量和 i 到 1 需要的額外油量,那麼有 begin pre i pre a w suf i suf a i w end 從 i 向右走到 j 需要的額外油量是 pre j pre ...

CF 242E 線段樹區間亦或 求和

題目鏈結 題意 給你n個數,有兩種操作,op 1,對從l到r的數求和,op 2,對從l到r的值xor val。思路 由於亦或是位運算,我們可以考慮位運算的關係,1 xor 1 0,0 xor 1 1,1 xor 0 1 0 xor 0 0 可以看出0 xor x x 1 xor 1 0,1 xor ...

CF719E(線段樹 矩陣快速冪)

題意 給你乙個數列a,a i 表示斐波那契數列的下標為a i 求區間對應斐波那契數列數字的和,還要求能夠維護對區間內所有下標加d的操作 分析 線段樹 線段樹的每個節點表示 f i f i 1 這個陣列 因為矩陣的可加性,所以可以進行lazy操作 我最開始的想法是每個節點lazy表示該區間下標加了多少...