#P1780. 2024.03.31-第1题-基环树
-
1000ms
Tried: 125
Accepted: 19
Difficulty: 4
所属公司 :
字节
时间 :2024年3月31日
-
算法标签>并查集
2024.03.31-第1题-基环树
思路
首先添加一条边变成基环树,基环树中恰好有 n 条边,因此初始的 m = n - 1 才可能有合法方案。
之后考虑只能添加一条边,最多只能有两个连通块
-
一个连通块,这个连通块就是一棵树,那么不能连出一条自环,答案就是:2n×(n−1)−(n−1)
-
两个连通块,此时两个连通块必然是一棵树和一棵基环树,大小为 n1 和 n2。那么答案为 n1×n2
时间复杂度:O(nlogn)
代码
java
import java.util.*;
public class Main {
static int[] pre;
static int find(int x) {
if (pre[x] == x) {
return x;
}
pre[x] = find(pre[x]);
return pre[x];
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
if (m != n - 1) {
System.out.println(0);
return;
}
pre = new int[n];
for (int i = 0; i < n; i++) {
pre[i] = i;
}
for (int i = 1; i < n; ++i) {
int u = scanner.nextInt() - 1;
int v = scanner.nextInt() - 1;
int fu = find(u);
int fv = find(v);
if (fu != fv) {
pre[fu] = fv;
}
}
int cnt = 0;
int[] siz = new int[n];
for (int i = 0; i < n; ++i) {
if (find(i) == i) {
cnt += 1;
}
siz[find(i)] += 1;
}
if (cnt > 2) {
System.out.println(0);
} else {
if (cnt == 1) System.out.println(n * (n - 1L) / 2 - (n - 1));
else {
long a = -1, b = -1;
for (int v : siz) {
if (v > 0) {
if (a == -1)
a = v;
else
b = v;
}
}
System.out.println(a * b);
}
}
}
}
C++
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int fa[N];
int mfind(int x){
if(fa[x]!=x){
fa[x]=mfind(fa[x]);
}
return fa[x];
}
void join(int x,int y){
int fx=mfind(x);
int fy=mfind(y);
if(fx!=fy){
fa[fx]=fy;
}
}
int main(){
int n,m;
cin>>n>>m;
if(m<n-1){
cout<<0<<endl;
return 0;
}
for(int i=1;i<=n;i++){
fa[i]=i;
}
bool flg=true;
for(int i=0;i<m;i++){
int u,v;
cin>>u>>v;
if(mfind(u)==mfind(v)) flg=false;
join(u,v);
}
int numtree=0;
unordered_map<int,int> h;
for(int i=1;i<=n;i++){
h[mfind(i)]++;
if(mfind(i)==i){
numtree++;
}
}
if(numtree>2){
cout<<0<<endl;
}else{
if(numtree==1){
if(flg){
cout<<1ll*n*(n-1)/2-(n-1)<<endl;
}else{
cout<<0<<endl;
}
}else{
int res=1;
for(auto [k,v]:h){
res*=v;
}
cout<<res<<endl;
}
}
// if(numtree>2){
// cout<<0<<endl;
// }else if(!flg){
// if(numtree==1){
// cout<<0<<endl;
// }else{
// int res=1;
// for(auto [k,v]:h){
// res*=v;
// }
// cout<<res<<endl;
// }
// }else{
// cout<<n*(n-1)/2-(n-1)<<endl;
// }
return 0;
}
python
class DisjointSet:
def __init__(self, n):
self.pa = [i for i in range(n)] # Correct instance variable initialization
def find(self, u):
# Find the representative of the set containing u with path compression
if self.pa[u] != u:
self.pa[u] = self.find(self.pa[u])
return self.pa[u] # Correct return variable
def union(self, u, v):
pu = self.find(u)
pv = self.find(v)
if pu != pv:
self.pa[pu] = pv
# Input handling
n, m = map(int, input().split())
disjointset = DisjointSet(n)
for i in range(m):
u, v = map(int, input().split())
disjointset.union(u-1, v-1)
# Calculate the number of connected components
cop = 0
for i in range(n):
if disjointset.find(i) == i:
cop += 1
if cop == 1:
if m >=n:
print(0)
else:
print((n - 1) * n // 2 - (n-1))
elif cop == 2:
dic = {}
for i in range(n):
root = disjointset.find(i)
if root not in dic:
dic[root] = 0
dic[root] += 1
ans = 1
for k in dic:
v = dic[k]
ans *= v
print(ans)
else:
print(0)
题目描述
定义基环树为n个点、条边且不包含重边和自环的无向连通图。形象化的描述,基环树可以由一棵树再添加一条边后形成(不能是树上已存在的边)现在薯条哥拿到了一个无向图,她想连一条边使得这个无向图变成基环树。
薯条哥想知道,有多少种不同的连边方案?
输入描述
第一行输入两个正整数n,m(1≤n,m≤105),代表> 无向图的点数和边数。 接下来的m行,每行输入两个正整数u,v(1≤u,v≤n),代表节点u和节点v有一条边连接。保证给定的无向图不包含重边和自环。
输出描述
输出一个整数,代表添加边的方案数。
样例1
输入
4 4
1 2
1 3
2 3
2 4
输出
0
样例解释
本身该无向图已经是基环树,因此方家数为0。
样例2
输入
4 3
1 2
1 3
2 3
输出
3
样例解释
第一个方案:连接1和4。
第二个方案:连接2和4。
第三个方案:连接3和4。