最小生成树1(模板)
问题描述
给定一个包含 nn 个顶点和 mm 条边的无向图。图中可能存在重边和自环,且边的权值可能为负数。你需要求出最小生成树(MST)的边权重之和。如果无法构造最小生成树,则输出 "impossible"。
最小生成树:在一个无向图中,由 nn 个顶点和 n−1n−1 条边构成的连通子图,且该子图的边权重之和最小。
如果图不连通,无法形成最小生成树。
输入格式
第一行输入二个正整数 n,mn,m。
接下来 mm 行,每行输入 33 个正整数 a,b,ca,b,c。表示点 aa 到点 bb 存在一条无向边,权值为 cc。
2≤n≤500,1≤m≤105,1≤a,b≤n,1≤c≤1042≤n≤500,1≤m≤105,1≤a,b≤n,1≤c≤104。
输出格式
输出一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出 "impossible"。
样例输入
4 5
1 2 1
1 3 2
1 4 3
2 3 2
3 4 4
样例输出
6


import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.*;
public class Main {
static int N=2*100010;
static int n,m;
static int id=1;
static int dist[]=new int[N];
static boolean isSure[]=new boolean[N];
static int h[]=new int[N];
static int e[]=new int[N];
static int ne[]=new int[N];
static int w[]=new int[N];
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
public static void main(String[] args) throws IOException {
StringTokenizer st=new StringTokenizer(br.readLine());
n=Integer.parseInt(st.nextToken());
m=Integer.parseInt(st.nextToken());
for (int i = 0; i < m; i++) {
st=new StringTokenizer(br.readLine());
int a=Integer.parseInt(st.nextToken());
int b=Integer.parseInt(st.nextToken());
int c=Integer.parseInt(st.nextToken());
add(a,b,c);add(b,a,c);
}
Arrays.fill(dist, Integer.MAX_VALUE);
dist[1]=0;
PriorityQueue<Node> priorityQueue=new PriorityQueue<Node>();
priorityQueue.add(new Node(1,0));
int cnt=0;//一共选中几个点了
int res=0;
while(cnt<n && !priorityQueue.isEmpty()){
Node no=priorityQueue.poll();
int u=no.x;
if(!isSure[u]){
res+=dist[u];
isSure[u]=true;cnt++;
for (int i = h[u]; i > 0; i=ne[i]) {
int son=e[i];
if(!isSure[son]){
if(dist[son]>w[i]){//这里表示的是到选中的集合的距离
dist[son]=w[i];
priorityQueue.add(new Node(son,dist[son]));
}
}
}
}
}
if(cnt!=n){
bw.write("impossible");
}else{
bw.write(res+"");
}
br.close();
bw.flush();
bw.close();
}
static class Node implements Comparable<Node>{
int x;
int dis;
public Node() {}
public Node(int x, int dis) {
this.x = x;
this.dis = dis;
}
@Override
public int compareTo(Node o) {
// TODO Auto-generated method stub
return this.dis-o.dis;
}
}
static void add(int a,int b,int c){
e[id]=b;
ne[id]=h[a];
w[id]=c;
h[a]=id++;
}
}
最小生成树2(模板)
给定一个包含 nn 个顶点和 mm 条边的无向图。图中可能存在重边和自环,且边的权值可能为负数。你需要求出最小生成树(MST)的边权重之和。如果无法构造最小生成树,则输出 "impossible"。
最小生成树:在一个无向图中,由 nn 个顶点和 n−1n−1 条边构成的连通子图,且该子图的边权重之和最小。
如果图不连通,无法形成最小生成树。
输入格式
第一行输入二个正整数 n,mn,m。
接下来 mm 行,每行输入 33 个正整数 a,b,ca,b,c。表示点 aa 到点 bb 存在一条无向边,权值为 cc。
2≤n≤105,1≤m≤2×105,1≤a,b≤n,1≤c≤1042≤n≤105,1≤m≤2×105,1≤a,b≤n,1≤c≤104。
输出格式
输出一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出 "impossible"。
样例输入
4 5
1 2 1
1 3 2
1 4 3
2 3 2
3 4 4
样例输出6

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.*;
public class Main {
static int N=2*100010;
static int n,m;
static int id=1;
static Node node[]=new Node[N];
static int p[]=new int[N];//并查集判断有没有环
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
public static void main(String[] args) throws IOException {
StringTokenizer st=new StringTokenizer(br.readLine());
n=Integer.parseInt(st.nextToken());
m=Integer.parseInt(st.nextToken());
for (int i = 1; i <= n; i++) {
p[i]=i;
}
PriorityQueue<Node> priorityQueue=new PriorityQueue<>();
for (int i = 0; i < m; i++) {
st=new StringTokenizer(br.readLine());
int a=Integer.parseInt(st.nextToken());
int b=Integer.parseInt(st.nextToken());
int c=Integer.parseInt(st.nextToken());
priorityQueue.add(new Node(a,b,c));
}
int cnt=0;//边数
int res=0;
while(cnt<n && !priorityQueue.isEmpty()){
Node no=priorityQueue.poll();
int a=no.a,b=no.b,c=no.dis;
if(find(a)!=find(b)){
res+=c;cnt++;
union(a,b);
}
}
if(cnt==n-1){
bw.write(res+"");
}else{
bw.write("impossible");
}
br.close();
bw.flush();
bw.close();
}
static void union(int a,int b){
int pa=find(a),pb=find(b);
p[pa]=pb;
}
static int find(int u){
if(u!=p[u]){
p[u]=find(p[u]);
}
return p[u];
}
static class Node implements Comparable<Node>{
int a;
int b;
int dis;
public Node() {
// TODO Auto-generated constructor stub
}
public Node(int a, int b, int dis) {
this.a = a;
this.b = b;
this.dis = dis;
}
@Override
public int compareTo(Node o) {
// TODO Auto-generated method stub
return dis-o.dis;
}
}
}
最近公共祖先(模板)
问题描述
给定一棵有 NN 个节点的树,每个节点有一个唯一的编号,从 11 到 NN。树的根节点是 11 号节点。接下来,你会得到 QQ 个查询。对于每个查询,你将得到两个节点的编号,你的任务是找到这两个节点的最低公共祖先。
输入格式
第一行包含一个整数 NN,表示树的节点数。
接下来的 N−1N−1 行,每行包含两个整数 UU 和 VV,表示节点 UU 和节点 VV 之间有一条边。
下一行包含一个整数 QQ,表示查询的数量。
接下来的 QQ 行,每行包含两个整数 AA 和 BB,表示你需要找到节点 AA 和节点 BB 的最低公共祖先。
输出格式
对于每个查询,输出一行,该行包含一个整数,表示两个节点的最近公共祖先。
样例输入
5
1 2
1 3
2 4
2 5
3
4 5
3 4
3 5
样例输出
2
1
1
样例说明
对于第一个查询,44 和 55 的最低公共祖先是 22。
对于第二个查询,33 和 44 的最低公共祖先是 11。
对于第三个查询,33 和 55 的最低公共祖先是 11。
测评数据规模
2≤N≤1052≤N≤105,1≤Q≤1041≤Q≤104,1≤U,V,A,B≤N1≤U,V,A,B≤N,题目保证输入的边形成一棵树。




import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.*;
public class Main {
static int N=2*100010;
static int n,m;
static int id=1;
static int log2[]=new int[N+1];
static int dep[]=new int[N];
static int f[][];
static int h[]=new int[N];
static int e[]=new int[N];
static int ne[]=new int[N];
static int w[]=new int[N];
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
public static void main(String[] args) throws IOException {
StringTokenizer st=new StringTokenizer(br.readLine());
n=Integer.parseInt(st.nextToken());
for (int i = 1; i < n; i++) {
st=new StringTokenizer(br.readLine());
int u=Integer.parseInt(st.nextToken()),v=Integer.parseInt(st.nextToken());
add(u,v);add(v,u);
}
log2[1]=0;
for (int i = 2; i < N; i++) {
log2[i]=log2[i/2]+1;
}
m=log2[N-1];
f=new int[N][m+1];
dfs(1,0);
st=new StringTokenizer(br.readLine());
int q=Integer.parseInt(st.nextToken());
for (int i = 0; i < q; i++) {
st=new StringTokenizer(br.readLine());
int u=Integer.parseInt(st.nextToken()),v=Integer.parseInt(st.nextToken());
if(u==v)bw.write(u+"");
else{
if(dep[u]<dep[v]){//设定u的深度更大
int t=u;u=v;v=t;
}
for (int j = m-1; j >= 0; j--) {
if(dep[f[u][j]]>=dep[v])u=f[u][j];
}
if(u==v){
bw.write(u+"\n");
}else{
for (int j = m-1; j >= 0; j--) {
if(f[u][j]!=f[v][j]){
u=f[u][j];
v=f[v][j];
}
}
bw.write(f[u][0]+"\n");
}
}
}
br.close();
bw.flush();
bw.close();
}
static void dfs(int u,int p){
dep[u]=dep[p]+1;
f[u][0]=p;
//2^(i)=2^(i-1)+2^(i-1)
for (int i = 1; i < m; i++) {
f[u][i]=f[f[u][i-1]][i-1];
}
for (int i = h[u]; i>0; i=ne[i]) {
int son=e[i];
if(son==p)continue;
dfs(son, u);
}
}
static void add(int a,int b){
e[id]=b;
ne[id]=h[a];
h[a]=id++;
}
}