【題解 | 兩種做法】洛谷 P4208 [JSOI2008] 最小生成樹計數 [矩陣樹/枚舉]

特別難調,洛谷題解區很多人代碼可讀性不強,做的我懷疑人生。

(雖然我的碼風也一般就是了)


前置知識:

Kruskal 求最小生成樹。

題面:

洛谷 P4208

兩種做法,一種矩陣樹一種枚舉。

(1)矩陣樹定理

還沒學過的指路這篇。

都知道矩陣樹定理能算生成樹個數,但本題要求最小生成樹個數,不能直接使用。

觀察發現:

同一無向連通圖中,不同最小生成樹各個權值的邊的數量相同的。

簡單證明下

如果存在兩個最小生成樹,一個選了?a-b?和?b-c?這兩條邊,

一個選了?a-d?和?d-c,其他邊都相同。

其中?a-b?的權值小于?a-d,而且兩對邊的權值和相同。

那我們就肯定可以選?a-b?和?d-c,這樣能得出更小的生成樹,矛盾。

(肯定有人會問:你怎么能假定倆生成樹其他邊一樣呢,難到不能通過其他邊到這四個點嗎?

? ? 笨,要是能到值還更小,那一開始不就選了嗎)

我們考慮先用 Kruskal 算法求出最小生成樹的邊集

對于權值為 i 的邊,把邊集里其他權值不為 i 的邊加到圖里,用并查集縮點

(因為每個權值的邊能減少的連通塊數量是固定的,只加最小生成樹里的就好。

? ? 絕對不能把邊集里所有權值不為 i 的邊一股腦全加進去!!那樣出來的就不是最小生成樹了!)

而邊集里所有權值為 i 的邊加到基爾霍夫矩陣里,在縮點的圖上求生成樹數量

(這個時候求生成樹就保證選的 i 權值邊的數量和一開始求最小生成樹 i 權值邊的數量一致!)

最后再把每個行列式乘到一起,就是答案。

時間復雜度:O(2^{10}M)

(M 是總邊數)

代碼思路不難,難的是調試,注意細節,別打錯了。

#include<bits/stdc++.h>
using namespace std;typedef long long LL;
const int N = 2e3 + 10;
const LL P = 31011;
int fa[N];int findfa(int x) {   //并查集路徑壓縮 if(fa[x] == x) {return x;}return fa[x] = findfa(fa[x]);
}struct node {int x, y;LL c;
} a[N];bool cmp(node na, node nb) {return na.c < nb.c;
}LL L[N][N];  //基爾霍夫/拉普拉斯矩陣 
void add(int x, int y) {L[x][y] --; L[y][x] --;L[x][x] ++; L[y][y] ++;
}int n, m;LL gauss(int nn) {   //高斯消元求行列式 nn--;int r = 1;LL res = 1;for (int c = 1; c <= nn; c++) {for (int i = r + 1; i <= nn; i++) {while (L[i][c]) {LL bs = L[r][c] / L[i][c];for (int j = 1; j <= nn; j++) {L[r][j] -= L[i][j] * bs;}swap(L[r], L[i]);res *= -1;}}if (L[r][c] != 0) {r ++;}}if (r <= nn) {   // 非連通圖,生成樹數量為0return 0;}for (int i = 1; i <= nn; i++) {res = res * L[i][i] %P;}return res;
}map<LL, LL> mp;   //用來判斷這條邊的權值在不在最小生成樹邊集里 
int b[N], e[N];   //b:縮點后點的編號,e:最小生成樹邊集 int main() {ios::sync_with_stdio(false);cin.tie(0);cin >> n >> m;for (int i = 1; i <= m; i++) {cin >> a[i].x >> a[i].y >> a[i].c;}for (int i = 1; i <= n; i++) {   //并查集初始化 fa[i] = i;}int len = 0;sort (a + 1, a + m + 1, cmp);for (int i = 1; i <= m; i++) {   //先跑一遍 Kruskal int tx = findfa(a[i].x);int ty = findfa(a[i].y);if (tx != ty) {mp[a[i].c] = 1;fa[tx] = ty;e[++len] = i;}}LL ans = 1;for (int i = 1; i <= len; i++) if(mp[a[e[i]].c]) {for (int j = 1; j <= n; j++) {fa[j] = j;     //再初始化一編,因為除了權值為 a[e[i]].c 邊還要跑一遍縮點 }for (int j = 1; j <= len; j++) {if(a[e[j]].c != a[e[i]].c) {int tx = findfa(a[e[j]].x);int ty = findfa(a[e[j]].y);if (tx != ty) {fa[tx] = ty;}}}int tmp = 0;  //縮點后有幾個點 for (int j = 1; j <= n; j++) if (findfa(j) == j) {tmp ++;b[j] = tmp;}memset(L, 0, sizeof(L));for (int j = 1; j <= m; j++) {if(a[j].c == a[e[i]].c) {int tx = findfa(a[j].x);int ty = findfa(a[j].y);if(b[tx] != b[ty]) {   //不在一個連通塊里 add(b[tx], b[ty]);   //加到基爾霍夫矩陣里 }}else if(a[j-1].c == a[e[i]].c){break;    //邊集已經排過序,可以直接退出 }}ans = ans * gauss(tmp) %P;   //乘法原理行列式 mp[a[e[i]].c] = 0;   //遍歷過就等于 0}cout << ans << "\n";return 0;
}

(2)dfs 枚舉

首先還是?Kruskal 算法確定最小生成樹,并統計每種權值的數量

對于每種權值,深搜枚舉該權值的邊是否選擇,最終返回可行方案數

將所有權值的方案數相乘,得到總的最小生成樹數量

時間復雜度:O(Mlog\,M+N^3)

(M 是總邊數,N 是點數)

直接看代碼吧,我寫了注釋:

#include<bits/stdc++.h>
using namespace std;typedef long long LL;
const int N = 2e3 + 10;
const LL P = 31011;struct node{int x, y;LL c;
} a[N];bool cmp(node na, node nb) {return na.c < nb.c;
}map<LL, LL> mp;  //存邊權對應離散值的 
int n, m;int fa[N];
int findfa(int x) {   //并查集 if (fa[x] == x) {return fa[x];}return fa[x] = findfa(fa[x]);
}LL num[N], res;void dfs(int now, int cnt, LL nowc) {  //now:當前節點,cnt:當前權值選了幾條邊,nowc:當前權值 if (cnt == num[mp[nowc]]) {   //選夠了就退出 res = (res + 1) %P;return ;}if (a[now].c != nowc) {  //越界了,選到別的權值區域 return ;}int pre[N];  //存檔 fa數組,一定一定要在函數內定義!!不然迭代之前的數據就不見了 for (int i = 1; i <= n; i++) {   pre[i] = fa[i];}int tx = findfa(a[now].x);int ty = findfa(a[now].y);if (tx != ty) {fa[tx] = ty;dfs(now + 1, cnt + 1, nowc);   //把當前邊加進去 }for (int i = 1; i <= n; i++) {fa[i] = pre[i];}dfs(now + 1, cnt, nowc);   //不加當前邊 
}int main() {ios::sync_with_stdio(false);cin.tie(0);cin >> n >> m;int len = 0;for (int i=1 ; i <= m; i++) {cin >> a[i].x >> a[i].y >> a[i].c;if (!mp[a[i].c]) {len ++;mp[a[i].c] = len;   //邊權離散值 }}sort(a + 1, a + m + 1, cmp);for (int i = 1; i <= n; i++) {fa[i] = i;     //并查集初始化 }int sum = 0;memset (num, 0, sizeof(num));for (int i = 1; i <= m; i++) {  //Kruskalint tx = findfa(a[i].x);int ty = findfa(a[i].y);if (tx != ty) {sum ++;num[mp[a[i].c]] ++;fa[tx] = ty;}}if (sum < n - 1) {cout << "0" << "\n";return 0;}for (int i = 1; i <= n; i++) {fa[i] = i;    //再來 }LL ans = 1;for (int i = 1; i <= m; i++) if(mp[a[i].c]) {  //還沒被 dfs過的最小生成樹權值 res = 0;dfs(i, 0, a[i].c);   ans = ans * res %P;for (int j = i; j <= m; j++) {if (a[j].c == a[i].c) {   //把當前權值的邊都加進去 int tx = findfa(a[j].x);int ty = findfa(a[j].y);if (tx != ty) {fa[tx] = ty;}	}else if (a[j - 1].c == a[i].c) {   //越界 break;}}mp[a[i].c] = 0;    //dfs過了當前權值,之后就不用了 }cout << ans << "\n";return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/95051.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/95051.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/95051.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

光譜相機多層鍍膜技術如何提高透過率

光譜相機多層鍍膜技術通過精密的光學設計與材料組合實現透過率提升&#xff0c;其核心原理與技術特性如下&#xff1a;一、多層鍍膜的光學優化機制?復合相位調控? 通過交替沉積高折射率&#xff08;如TiO?, n2.3&#xff09;與低折射率材料&#xff08;如SiO?, n1.46&#…

ubantu安裝配置hive

在Ubuntu系統上安裝Hive通常涉及幾個步驟&#xff0c;包括安裝Java&#xff08;因為Hive依賴于Java&#xff09;&#xff0c;安裝Hadoop&#xff0c;然后安裝Hive本身。以下是一個基本的步驟指南&#xff1a; 安裝Java 首先&#xff0c;確保你的系統上安裝了Java。你可以通過運…

大模型RAG項目實戰:文本向量模型>Embedding模型、Reranker模型以及ColBERT模型

注&#xff1a;此文章內容均節選自充電了么創始人&#xff0c;CEO兼CTO陳敬雷老師的新書《GPT多模態大模型與AI Agent智能體》&#xff08;跟我一起學人工智能&#xff09;【陳敬雷編著】【清華大學出版社】 清華《GPT多模態大模型與AI Agent智能體》書籍配套視頻課程【陳敬雷…

基于uni-app的校園綜合服務平臺開發實戰

閃遞校園&#xff1a;基于uni-app的校園綜合服務平臺開發實戰作為一名全棧開發者&#xff0c;我用6個月時間開發了這款校園綜合服務平臺——閃遞校園。本文將詳細分享項目從0到1的開發經驗&#xff0c;包括技術選型、核心功能實現、踩坑記錄以及性能優化等方面的干貨內容。&…

Qt::Q_INIT_RESOURCE用法

q_init_resource 用法 q_init_resource 是 Qt 框架中用于初始化嵌入式資源的一個函數。它通常用于將編譯到應用程序二進制文件中的資源&#xff08;如圖像、QML文件、翻譯文件等&#xff09;注冊到Qt的資源系統中。 基本用法 cpp Q_INIT_RESOURCE(resourcename); 其中 resource…

【開題答辯全過程】以 基于php的校園兼職求職網站為例,包含答辯的問題和答案

個人簡介一名14年經驗的資深畢設內行人&#xff0c;語言擅長Java、php、微信小程序、Python、Golang、安卓Android等開發項目包括大數據、深度學習、網站、小程序、安卓、算法。平常會做一些項目定制化開發、代碼講解、答辯教學、文檔編寫、也懂一些降重方面的技巧。感謝大家的…

安卓懸浮球-3566-測試報告

安卓懸浮球-3566-測試報告 測試概述 項目名稱: 懸浮球電子秤應用 測試版本: v1.0.0 測試時間: 2025年9月 測試環境: UniApp開發環境 測試類型: 功能測試、性能測試、兼容性測試 測試結果: 見附件測試環境配置 硬件環境 測試設備: Android 內置3566屏幕分辨率: 1080x1920內存: 2…

《C++進階之STL》【紅黑樹】

【紅黑樹】目錄前言&#xff1a;------------概念介紹------------1. 什么是紅黑樹&#xff1f;2. 紅黑樹的基本特性是什么&#xff1f;3. 紅黑樹的效率怎么樣&#xff1f;4. 紅黑樹如何確保最長路徑不超過最短路徑的2倍&#xff1f;------------基本操作------------一、查找操…

Java全棧工程師的實戰面試:從基礎到微服務

Java全棧工程師的實戰面試&#xff1a;從基礎到微服務 在一次真實的面試中&#xff0c;一位經驗豐富的Java全棧開發工程師被問及多個技術問題。他的名字是林浩然&#xff0c;28歲&#xff0c;擁有計算機科學與技術碩士學位&#xff0c;擁有5年的工作經驗。他曾在一家大型互聯網…

工業物聯網(IIoT)+ AI:智能工業的未來趨勢全解析

工業物聯網&#xff08;IIoT&#xff09; AI&#xff1a;智能工業的未來趨勢全解析 文章目錄工業物聯網&#xff08;IIoT&#xff09; AI&#xff1a;智能工業的未來趨勢全解析摘要什么是工業物聯網&#xff08;IIoT&#xff09;&#xff1f;1. IIoT 的定義2. IIoT 與傳統 IoT …

3000. 對角線最長的矩形的面積

3000. 對角線最長的矩形的面積 題目鏈接&#xff1a;3000. 對角線最長的矩形的面積 代碼如下&#xff1a; class Solution { public:int areaOfMaxDiagonal(vector<vector<int>>& dimensions) {double maxDiagonalLength 0;int res 0;for (vector<int&g…

Scikit-learn Python機器學習 - 什么是機器學習

鋒哥原創的Scikit-learn Python機器學習視頻教程&#xff1a; 2026版 Scikit-learn Python機器學習 視頻教程(無廢話版) 玩命更新中~_嗶哩嗶哩_bilibili 課程介紹 本課程主要講解基于Scikit-learn的Python機器學習知識&#xff0c;包括機器學習概述&#xff0c;特征工程(數據…

Python環境搭建報錯

檢查Python版本兼容性確保下載的Python版本與操作系統匹配&#xff08;如Windows 32位/64位、macOS ARM/x86&#xff09;。可通過命令行輸入python --version或python3 --version驗證已安裝版本是否與需求一致。清理殘留文件若之前安裝失敗&#xff0c;需手動刪除殘留文件。Win…

C# WinForm中提供webapi服務

云川給我提了一個需求&#xff0c;要我開發一個API服務程序&#xff0c;他來調用&#xff0c;程序再去明道云取數&#xff0c;計算出一個結果返回。網上找到了一篇文章&#xff1a;C# 在Windform程序中搭建Webapi - 小碼哥-風云 - 博客園&#xff0c;可以使用微軟提供的Microso…

Linux中使用docker部署solr

1. 運行一次&#xff0c;然后拉取鏡像 [rootinstance-yo4hab98 ~]# docker run -d -p 8983:8983 --name solr-8.11.3 -t solr:8.11.3 ps 鏡像相關指令 # 查看鏡像 docker images# 刪除鏡像 指定名稱和版本刪除 docker rmi nginx:latest # 刪除鏡像 指定id刪除 docker rm…

代謝組學分析指南

摘要代謝組學是個新興領域&#xff0c;系統性地定量眾多代謝物。關鍵目的是識別與每種生物表型相對應的代謝物&#xff0c;并進一步分析其中涉及的機制。盡管代謝組學對于理解相關的生物學現象至關重要&#xff0c;但在全面描述過程的能力上存在局限性。推薦采用綜合分析策略&a…

vue2使用el-form動態參數展示并非空校驗

需求&#xff1a;需要根據類型type動態顯示某些參數&#xff0c;并且后端需要的參數也不同&#xff0c;比如type為1&#xff1a;后端要aa和bb參數&#xff0c;type為2&#xff1a;后端要cc和dd參數&#xff0c;前端顯示的字段名也不一樣&#xff0c;但是樣式是不變的。1.效果2.…

(附源碼)基于Vue的教師檔案管理系統的設計與實現

摘 要 隨著信息技術的不斷發展&#xff0c;學校管理工作正逐漸從紙質化向數字化轉型。教師檔案管理作為學校管理的重要環節&#xff0c;其信息化和高效化對于提升學校管理水平具有重要意義。本文設計并實現了一個基于Vue框架的教師檔案管理系統&#xff0c;旨在通過前端技術的…

運算電源抑制比(PSRR)測量及設計注意事項

1、簡介如果運放的供電電源發生變化&#xff0c;輸出不應發生變化&#xff0c;但實際運放隨著供電電源的波動&#xff0c;運放輸出也將會發生波動。折合到輸出端&#xff0c;PSRR定義 Xv(電源電壓波動) / Yv&#xff08;輸出電壓波動&#xff09;&#xff0c;該量為無量綱&…

YOLOv8-SMOT:一種高效魯棒的實時小目標跟蹤框架:基于切片輔助訓練與自適應關聯

https://arxiv.org/pdf/2507.12087 摘要 從無人機&#xff08;UAV&#xff09;視角對小型敏捷多目標&#xff08;SMOT&#xff09;——例如鳥類——進行跟蹤是一項極具挑戰性的計算機視覺任務。該任務的難點主要源于三個方面&#xff1a;目標外觀特征極度稀缺、相機與目標自身復…