最小生成樹專項

spdarkle發表於2024-10-08

contests-link

A

求最短路啊

那顯然只需要看端點顏色不同的邊即可

那麼依次考慮每條邊的貢獻

一個想法是暴力修改,不過菊花就死了

一個想法是把顏色相同且相連的點縮在一起然後求剩下邊的min,現在至少剩下兩個連通塊

那根據Boruvka知道,這剩下的最優邊必然是MST上的邊(對於n個點任意劃分為若干個集合,不同集合兩兩之間最小邊的最小值一定在MST上

建出Kruskal重構樹?一個點造成貢獻當且僅當子樹內顏色個數不少於2?

也不太好感覺,可以支援加不支援刪

暴力的話就是邊太多就死了

由於我們求了MST,不妨一個點維護其到父親的邊的可行性,那麼可以類比點分樹的計算方法,我們每個點維護到兒子的所有邊的貢獻,這樣可以將點權影響的邊變為只需要整體改動和單點修改

發現邊應當按照顏色分類,所以我們利用 set map<int,multiset<int>> 存下每個點的每個兒子的邊按照顏色分類後的邊權最值。

這樣的話,答案可以對於每個點單獨算,而更改一個點的顏色,相當於是更改了那個點的2個set 對答案的貢獻狀態,可以 \(O(\log n)\) 做,而考慮對父親的也是單點修改。

B

考慮支配關係

如果有 \(a<b<c\) 其兩兩 \(\gcd\) 相同,則顯然用 \(a-b,a-c\) 即可,也即 \(b-c\) 是無用的

這就被支配了

列舉 \(\gcd\) 即可

\(O(n\log n)\) 條邊

C

根據差分的知識,原陣列全零相當於差分陣列全零,那麼可以對於每個點求出 \(l,r\),則可以修改 \(l,r+1\) 兩個單點(聯動修改)

則所有修改的單點連通即可,跑 Kruskal 就 OK 了

D

沒啥好說的,線段樹分治,每一層扔掉不可能的邊和一定能的邊即可,其餘遞迴解決,複雜度雙 \(\log\)

不可能的邊指:假定子樹內將來會用到的所有邊邊權全是 \(inf\),也不可能用到的邊(也就是本節點涉及的邊跑 Kruskal,扔掉無法更新的)。

一定能的邊指:假設子樹內將來會用到的邊邊權全是 \(-inf\),也會用到的邊(也就是先加入所有的將來邊,然後再跑Kruskal仍然能夠產生貢獻的邊)

顯然,這樣處理之後剩下的邊只有 \(O(len)\) 條,下傳到左右兒子即可。

很經典。

void sol(int x,int l,int r){
    sort(ad[x].begin(),ad[x].end());
    if(l==r){
        ll lst=dsu::top,tmp=res;
        for(auto [u,v,w]:ad[x])res+=dsu::merge(u,v)*w;
        ans[l]=res;res=tmp;dsu::del(lst);return ;
    }
    ll lst=dsu::top,tmp=res,mid=l+r>>1;
    vector<bool>ud(ad[x].size());
    for(auto [u,v,w]:pas[x])dsu::merge(u,v);
    for(int i=0;i<ad[x].size();++i){
        auto [u,v,w]=ad[x][i];
        if(dsu::merge(u,v))ud[i]=1;
    }
    dsu::del(lst);
    for(int i=0;i<ad[x].size();++i){
        if(ud[i]){dsu::merge(ad[x][i].u,ad[x][i].v);res+=ad[x][i].w;continue;}
        if(dsu::merge(ad[x][i].u,ad[x][i].v)){
            ad[lc].push_back(ad[x][i]);
            ad[rc].push_back(ad[x][i]);
        }
    }
    dsu::del(lst);
    for(int i=0;i<ad[x].size();++i)if(ud[i])dsu::merge(ad[x][i].u,ad[x][i].v);
    ad[x].clear();ad[x].shrink_to_fit();pas[x].clear();pas[x].shrink_to_fit();
    sol(lc,l,mid);sol(rc,mid+1,r);
    res=tmp;dsu::del(lst);
}

E

顯然對於值相同的兩個點連在一起是最優的,所以可以去掉相同值的貢獻,也就是每個值只保留一個元素。

然後由於求最大生成樹,考慮Kruskal,從大到小列舉邊權,能連的一定連。

則假設現在列舉的邊權是 \(x\),還剩下 \(a_1\sim a_m\) 滿足 \(a_i\& x=x\)

則必然有 \(\forall i,j,a_i\&a_j=x\),否則已經提前處理。

且由於這個條件,可以推出 \(m\le \log V\),因為鴿巢原理( \(x\) 的零位最多每個位放一個數到極限了)。

所以可以暴力找掃一遍所有數合併即可。

找數這個步驟,可以在做 Kruskal 的同時做高維字尾和。

當然你可以 Boravka,樸素的想法是計算高維字尾和找最優決策,事實上 Trie 處理按位與往往將“1”子樹與“0”子樹合併在一起作為新的零子樹,按位或同理(像線段樹合併一樣)每個點最多合併 \(\log V\) 次。

那麼相當於每個點有顏色,你Trie維護樹內不同顏色個數即可。

    for(int i=(1<<d)-1;i>=0;--i){
		if(!a[i]){
			for(int j=0;j<d;++j)if(a[i|(1<<j)]){
				a[i]=a[i|(1<<j)];break;
			}
		}
		if(!a[i])continue;
		for(int j=0;j<d;++j)if(a[i|(1<<j)]&&find(a[i])!=find(a[i|(1<<j)])){
			ans+=i;f[find(a[i])]=find(a[i|(1<<j)]);
		}
	}

F

首先可以給邊賦權之後轉化為WQS二分板子

另一個巧妙的想法是拿出必須邊,也就是假定用所有白邊後仍然需要的黑邊,用所有黑邊後仍然需要的白邊。

這樣的必經邊拿出來後我們就能夠保證連通了,接下來貪心先處理白邊,在次數充足時貪心能加就加,次數用完了就加黑邊即可。

G

將一個值的 \(x,y\) 拿出來,相當於是做變換

\(C\) 時,每個值的 \(x\) 已經歸位,也就是每個行號已經OK了

那麼可以發現得到一個 \(B\) 之後是可以求出 \(C\) 的,因為變換已經固定了。

所以就必須要滿足 \(B\) 裡每一列的行號不同,且滿足後一定可行,否則就沒辦法重排。

所以我們只需要做 \(A\to B\) 這一步就好了,滿足只移動每一行,然後使得每一列元素行號不同。

有點匹配的味道了

我們只關心每個元素的行號,要透過移動每一行是的每一列的元素值互不相同。

如果建立行列二分圖,相當於是每一個列到每一行的邊都染為不同顏色

而這個操作移動操作相當於是交換了一個行的兩條出邊。

你考慮依次調整每個顏色

這裡顯然有對稱性,所以你的開始位置不重要

不過這裡也可以看作行號相同的兩個點不在同一列上

如果說割開看,相當於是把行號相同的 \(m\) 個元素拿出來,考慮一次性歸位一種顏色,然後把 \(m\) 列拿出來組成一個二分圖匹配,做 \(n\) 輪就行了,而且這玩意是正則圖?

這玩意有後效性應該假了。

那麼考慮每次求一列的答案

你對行和顏色建立二分圖,行i向其包含的顏色 \(j\) 連線 \(c_{i,j}\) 條邊表示該行顏色出現次數。

匹配後就可以拿出一列的答案,如果匹配完美

而且匹配完了之後左部右部每個點的度數全部減少 \(1\),且初始度數全部是 \(n\),局面本質不會改變(霍爾定理始終滿足(每個點度數相同,又顯然有 \(|E(N(S))|\ge |E(S)|\),而 \(\forall S, |E(S)|=k·|S|\),其中 \(k\) 是常數,所以有 \(|N(S)|\ge |S|\),隨便拿出一組匹配之後是沒有後效性的)),那就完了。

事實上這是正則圖。

建立 \(k\) 正則二分圖,隨意拿出一組完美匹配不會影響局面,只會變為 \(k-1\) 正則二分圖

不知道這個說法對不對,反正感覺說出來挺舒服,這個東西好像叫正則二分圖 \(k\) 染色

輸出方案真噁心。。。

使用網路流可以做到 \(O(m·(n\sqrt {n^2}))=O(mn^2)\)

H

二分圖匹配模板題。

相關文章