首先感謝題解小哥,他在標算外又總結了三種做法。
此處僅提及最後一種做法。
首先考慮題目中要求的所有結點度數為奇數的限制。
對於每乙個聯通塊,因為所有結點總度數是偶數,所以總結點數也必須是偶數的。即所有聯通塊都要是偶數大小。
而考慮任意乙個偶數大小的聯通塊,我們任意取它的乙個生成樹,然後進行如下演算法:
設 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表示該區間下標加了多少...