第三章 图论
包括单源最短路的建图方式、单源最短路的综合应用、单源最短路的扩展应用、Floyd算法、最小生成树、最小生成树的扩展应用、负环、差分
约束、最近公共祖先、强连通分量、双连通分量、二分图、欧拉回路和欧拉路径、拓扑排序等内容
dijikstra基于贪心
spfa/bf/floyd基于dp
单源最短路的建图方式
AcWing 1129. 热浪
1129
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 2510,M=6200*2+10;//N是点,M是边
int n,m,S,T;//点,边,起点,终点
int h[N], e[M], w[M],ne[M], idx;//邻接表
int dist[N],q[N];//距离,循环队列
bool st[N];//判重数组
void add(int a, int b, int c) // 添加一条边a->b,边权为c
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
void spfa()
{
memset(dist,0x3f,sizeof dist);//初始化所有点的距离
dist[S]=0;
queue<int>q;//定义一个队列存储待更新的点
q.push(S);//把一号点放到队列中
st[S]=true;//st数组存的是当前这个点是不是在队列当中,防止我们队列当中储存重复的点
while(q.size())
{
int t=q.front();//取出队头
q.pop();//删掉队头
st[t]=false;//点从队列中出来了
for(int i=h[t];i!=-1;i=ne[i])//更新t的所有临边
{
int j=e[i];//用j表示当前这个点
if(dist[j]>dist[t]+w[i])//看一下这个点能不能更新
{
dist[j]=dist[t]+w[i];
if(!st[j])//如果j不在队列中
{
q.push(j);//把j加入队列
st[j]=true;//标记j已经在队列中
}
}
}
}
}
int main()
{
cin>>n>>m>>S>>T;
memset(h, -1, sizeof h);//初始化邻接表的表头
for(int i=0;i<m;i++)
{
int a,b,c;
cin>>a>>b>>c;
add(a, b, c),add(b,a,c);
}
spfa();
cout<<dist[T]<<endl;
return 0;
}
AcWing 1128. 信使
1128
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110,INF=0X3f3f3f3f;
int n,m;//点,边
int d[N][N];//距离
int main()
{
cin>>n>>m;
memset(d,0x3f,sizeof d);//初始化距离
for(int i=0;i<m;i++)
{
int a,b,c;
cin>>a>>b>>c;
d[a][b]=d[b][a]=min(d[a][b],c);//防止有重边,只保留最短的一条边
}
//Floyd
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
//求1号点到每个点的距离
int res=0;
for(int i=1;i<=n;i++)
if(d[1][i]==INF)
{
res=-1;
break;
}
else res=max(res,d[1][i]);
cout<<res<<endl;
return 0;
}
//求指挥部到每个点的最短距离,取一个最大值,如果是正无穷就输出-1,否则输出最大值
//最大值就是最晚接收到信息所需要的时间
//用Floyd写,代码最短,只要数据范围够小,就可以用多源最短路写单源最短路
AcWing 1127. 香甜的黄油
1127
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 810,M=3000,INF=0x3f3f3f3f;//边是1450,无向边,乘2
int n,p,m;//n是奶牛数量,p是牧场数量,m牧场间道路数
int id[N];//到N头奶牛所在的牧场号
int h[N], e[M], w[M], ne[M], idx;//边用邻接表存
int q[N], dist[N];//距离用dist存,q是循环队列
bool st[N];//判重数组
void add(int a, int b, int c) // 添加一条边a->b,边权为c
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
int spfa(int start)
{
memset(dist,0x3f,sizeof dist);//初始化所有点的距离
dist[start]=0;
queue<int>q;//定义一个队列存储待更新的点
q.push(start);//把一号点放到队列中
st[start]=true;//st数组存的是当前这个点是不是在队列当中,防止我们队列当中储存重复的点
while(q.size())
{
int t=q.front();//取出队头
q.pop();//删掉队头
st[t]=false;//点从队列中出来了
for(int i=h[t];i!=-1;i=ne[i])//更新t的所有临边
{
int j=e[i];//用j表示当前这个点
if(dist[j]>dist[t]+w[i])//看一下这个点能不能更新
{
dist[j]=dist[t]+w[i];
if(!st[j])//如果j不在队列中
{
q.push(j);//把j加入队列
st[j]=true;//标记j已经在队列中
}
}
}
}
int res=0;
for(int i=0;i<n;i++)
{
int j=id[i];
if(dist[j]==INF)return INF;//如果说某头牛到起点的距离为正无穷,说明当前这个起点不是答案
res+=dist[j];
}
return res;
}
int main()
{
cin>>n>>p>>m;
for(int i=0;i<n;i++)cin>>id[i];
memset(h, -1, sizeof h);
for(int i=0;i<m;i++)
{
int a,b,c;
cin>>a>>b>>c;
add(a, b, c),add(b,a,c);
}
//枚举每个点作为起点
//spfa返回所有点到起点的距离之和,传入的是起点
int res=INF;
for(int i=1;i<=p;i++)res=min(res,spfa(i));
cout<<res<<endl;
return 0;
}
AcWing 1126. 最小花费
//没太看懂,洛谷也有,明天研究一下
1126
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2010;
int n,m,S,T;
double g[N][N];//用邻接矩阵存,邻接表也可以
double dist[N];
bool st[N];
void dijkstra()
{
dist[S] = 1;
for(int i=1;i<=n;i++)//迭代n次
{
int t=-1;//找到当前没有确定最短路长度的点当中距离最小的那一个,t等于-1表示还没有确定好
for(int j=1;j<=n;j++)
if(!st[j]&&(t==-1||dist[t]<dist[j]))//没有确定最短路,并且t是-1或者距离不是最短的
t=j;
st[t]=true;//把t加到集合中
for(int j=1;j<=n;j++)
dist[j]=max(dist[j],dist[t]*g[t][j]);//1到t加t到j的长度更新1到j的长度,这里把加法变成乘法
}
}
int main()
{
cin>>n>>m;
while (m -- )
{
int a,b,c;
cin>>a>>b>>c;
double z=(100.0-c)/100;
g[a][b]=g[b][a]=max(g[a][b],z);
}
cin>>S>>T;
dijkstra();
printf("%.8lf",100/dist[T]);//dist是w1*w2*...*wn的值
return 0;
}
//使用朴素版的dijkstra算法
AcWing 920. 最优乘车 可以
从当前这个点到另一个点只需要做一次车,权重就是1
做两次车权重就是2
只需要求出来从1号点到n号点的最短路径
换车次数等于坐车次数减1
特判一下如果1号点和n号点重合,坐车次数和换车次数都是0
可以用BFS,所有边的权重都是1
法一
#include <cstring>
#include <iostream>
#include <algorithm>
#include <sstream>
using namespace std;
const int N = 510;//点
int m, n;//m条路线
bool g[N][N];//用邻接矩阵存边,true是有边,false没边
int dist[N];//距离
int stop[N];//公交线路的站牌
int q[N];
void bfs()
{
int hh = 0, tt = 0;
memset(dist, 0x3f, sizeof dist);
q[0] = 1;
dist[1] = 0;
while (hh <= tt)
{
int t = q[hh ++ ];//取出队头元素
for (int i = 1; i <= n; i ++ )//遍历这个点的所有临边
if (g[t][i] && dist[i] > dist[t] + 1)//如果他们之间有边
{
dist[i] = dist[t] + 1;
q[ ++ tt] = i;//把i加到队列中
}
}
}
int main()
{
cin >> m >> n;
string line;//每条路线
getline(cin, line);//先把回车读掉
while (m -- )
{
getline(cin, line);//先把这一整行读进来
stringstream ssin(line);//把一整行传到stringstream中,stringstream相当于sscnaf
int cnt = 0, p;//cnt是公交线路的站牌的数量,p是站牌的编号
while (ssin >> p) stop[cnt ++ ] = p;
for (int j = 0; j < cnt; j ++ )//从公交线路的每个点出发,向他线路上后面所有点连一条边
for (int k = j + 1; k < cnt; k ++ )
g[stop[j]][stop[k]] = true;
}
bfs();
if (dist[n] == 0x3f3f3f3f) puts("NO");
else cout << max(dist[n] - 1, 0) << endl;
return 0;
}
法二Floyd
#include<iostream>
#include<cstring>
#include<sstream>
using namespace std;
const int N = 510;
int n, m, s[N][N];
int main()
{
cin >> m >> n;
memset(s, 0x3f, sizeof s);
getchar();
while (m--)
{
string line;
getline(cin, line);
stringstream scin(line);
int cnt = 0, t, stop[N];
while (scin >> t) stop[cnt++] = t;
//单程巴士
//每个汽车的前站达到后站的距离都为1 因为是同一辆车 未换车
for (int i = 0; i < cnt; i++)
for (int j = i + 1; j < cnt; j++)
s[stop[i]][stop[j]] = 1;
}
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
s[i][j] = min(s[i][j], s[i][k] + s[k][j]);
if (s[1][n] >= 0x3f3f3f3f) cout << "NO";
else cout << s[1][n] - 1;
}
法三dijkstra
// 123145.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <sstream>
#include <memory.h>
#include <queue>
using namespace std;
const int N = 510;
int n, m;
int stop[N];
/*
3 7
6 7
4 7 3 6
2 1 3 5
输出样例:
2
*/
int g[N][N];
int dist[N];
bool st[N];
int dij()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
for (int i = 0; i < n - 1; i++) {
int t = -1;
for (int j = 1; j <= n; j++) {
if (!st[j] && (t == -1 || dist[t] > dist[j])) {
t = j;
}
}
for (int j = 1; j <= n; j++) {
dist[j] = min(dist[j], dist[t] + g[t][j]);
}
st[t] = true;
}
return dist[n];
}
int main()
{
cin >> m >> n;
memset(g, 0x3f, sizeof g);
string line;
getline(cin, line);
while (m--) {
getline(cin, line);
stringstream ssin(line);
int cnt = 0, p;
while (ssin >> p) stop[cnt++] = p;
//每个汽车的前站达到后站的距离都为1 因为是同一辆车 未换车
for (int i = 0; i < cnt; i++) {
for (int j = i + 1; j < cnt; j++) {
g[stop[i]][stop[j]] = 1;
}
}
}
int ret = dij();
if ( ret > 0x3f3f3f3f/2) cout << "NO" << endl;
else cout << ret-1 << endl;
return 0;
}
法四spfa
// 123145.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <sstream>
#include <queue>
#include <vector>
#include <memory.h>
using namespace std;
const int N = 510;
int n, m;
int stop[N];
/*
3 7
6 7
4 7 3 6
2 1 3 5
输出样例:
2
*/
vector<pair<int, int>> g[N];
int dist[N];
bool st[N];
int spfa()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
queue<int> q;
q.push(1);
st[1] = true;
while (q.size()) {
int t = q.front();
q.pop();
st[t] = false;
for (int i = 0; i < g[t].size(); i++) {
int j = g[t][i].first;
int w = g[t][i].second;
if (dist[j] > dist[t] + w) {
dist[j] = dist[t] + w;
if (!st[j]) {
q.push(j);
st[j] = true;
}
}
}
}
return dist[n];
}
int main()
{
cin >> m >> n;
string line;
getline(cin, line);
while (m--) {
getline(cin, line);
stringstream ssin(line);
int cnt = 0, p;
while (ssin >> p) stop[cnt++] = p;
//每个汽车的前站达到后站的距离都为1 因为是同一辆车 未换车
for (int i = 0; i < cnt; i++) {
for (int j = i + 1; j < cnt; j++) {
g[stop[i]].push_back({ stop[j],1 });
}
}
}
int ret = spfa();
if (ret == 0x3f3f3f3f) cout << "NO" << endl;
else cout << ret -1 << endl;
return 0;
}
AcWing 903. 昂贵的聘礼 可以
可以通过枚举每一个可行的区间
限制等级不在区间内的点
每一次做一个最短路模型
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 110, INF = 0x3f3f3f3f;//N是点数
int n, m;
int w[N][N], level[N];//w是邻接矩阵,level是等级
int dist[N];
bool st[N];
//传入的是区间的范围
int dijkstra(int down, int up)
{
memset(dist, 0x3f, sizeof dist);//初始化距离
memset(st, 0, sizeof st);//清空st数组
dist[0] = 0;//最开始起点是虚拟原点0
for (int i = 1; i <= n + 1; i ++ )
{
int t = -1;
for (int j = 0; j <= n; j ++ )
if (!st[j] && (t == -1 || dist[t] > dist[j]))//t的距离大于j的距离
t = j;
st[t] = true;//标记t
for (int j = 1; j <= n; j ++ )//更新其他所有点
if (level[j] >= down && level[j] <= up)//只有当前这个点在等级范围内才会更新他
dist[j] = min(dist[j], dist[t] + w[t][j]);
}
return dist[1];
}
int main()
{
cin >> m >> n;
memset(w, 0x3f, sizeof w);//初始化距离
for (int i = 1; i <= n; i ++ ) w[i][i] = 0;//每个点到自己的距离初始化为0
for (int i = 1; i <= n; i ++ )
{
int price, cnt;//读入价格和替换的数量
cin >> price >> level[i] >> cnt;
w[0][i] = min(price, w[0][i]);//把虚拟原点定义成0号点,从虚拟原点向当前这个点连一条有向边,如果有重边,保留最小的边
while (cnt -- )
{
int id, cost;//替代品,和替换需要的价格(边的权重)
cin >> id >> cost;
w[id][i] = min(w[id][i], cost);//防止有重边
}
}
int res = INF;
//把一号点的等级包含进去,i枚举的是区间的较小值
for (int i = level[1] - m; i <= level[1]; i ++ ) res = min(res, dijkstra(i, i + m));
cout << res << endl;
return 0;
}
单源最短路的综合应用
AcWing 1135. 新年好 1135
AcWing 340. 通信线路 可以
AcWing 342. 道路与航线 可以
AcWing 341. 最优贸易 可以
单源最短路的扩展应用
AcWing 1137. 选择最佳线路 hdu
AcWing 1131. 拯救大兵瑞恩 1131
AcWing 1134. 最短路计数 1134
AcWing 383. 观光 可以
Floyd算法
AcWing 1125. 牛的旅行
1125
#include <iostream>
#include <cstring>
#include <algorithm>
#include<cmath>
#define x first
#define y second
using namespace std;
const int N = 150;
const double INF=1e20;
typedef pair<int, int> PII;
int n;//点
PII q[N];//坐标
double d[N][N],maxd[N];//d是距离
char g[N][N];
double get_dist(PII a,PII b)
{
double dx=a.x-b.x,dy=a.y-b.y;
return sqrt(dx*dx+dy*dy);
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)cin>>q[i].x>>q[i].y;
for(int i=0;i<n;i++)cin>>g[i];
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(i!=j)
{
if(g[i][j]=='1')d[i][j]=get_dist(q[i],q[j]);
else d[i][j]=INF;//不联通
}
for(int k=0;k<n;k++)
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
//算maxd
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(d[i][j]<INF)
maxd[i]=max(maxd[i],d[i][j]);
//情况一
double res1=0;
for(int i=0;i<n;i++)res1=max(res1,maxd[i]);
//情况二
double res2=INF;
for (int i = 0; i < n; i ++ )
for(int j=0;j<n;j++)
if(d[i][j]>=INF)//i和j不联通
res2=min(res2,get_dist(q[i],q[j])+maxd[i]+maxd[j]);
printf("%lf\n",max(res1,res2));
return 0;
}
AcWing 343. 排序 可以
AcWing 344. 观光之旅 可以
AcWing 345. 牛站 可以
最小生成树
AcWing 1140. 最短网络 1140
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110;
int n;
int w[N][N];
int dist[N];
bool st[N];
int prim()
{
int res=0;
memset(dist, 0x3f, sizeof dist);
dist[1]=0;
for(int i=0;i<n;i++)
{
int t=-1;
for(int j=1;j<=n;j++)
if(!st[j]&&(t==-1||dist[t]>dist[j]))
t=j;
res+=dist[t];
st[t]=true;
for(int j=1;j<=n;j++)dist[j]=min(dist[j],w[t][j]);
}
return res;
}
int main()
{
cin>>n;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
cin>>w[i][j];
cout<<prim()<<endl;
return 0;
}
AcWing 1141. 局域网 1141
有多个联通块也是对的
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 110, M = 210;
int n, m;
struct Edge
{
int a, b, w;
bool operator< (const Edge &t)const
{
return w < t.w;
}
}e[M];
int p[N];
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++ ) p[i] = i;
for (int i = 0; i < m; i ++ )
{
int a, b, w;
cin >> a >> b >> w;
e[i] = {a, b, w};
}
sort(e, e + m);
int res = 0;
for (int i = 0; i < m; i ++ )
{
int a = find(e[i].a), b = find(e[i].b), w = e[i].w;
if (a != b) p[a] = b;
else res += w;
}
cout << res << endl;
return 0;
}
有多个联通块是错的
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 510,INF=0x3f3f3f3f;
int n,m;
int g[N][N];
int dist[N];
bool st[N];
int sum;
int prim()
{
memset(dist,0x3f,sizeof dist);
int res=0;//长度之和
for(int i=0;i<n;i++)
{
int t=-1;
for(int j=1;j<=n;j++)
if(!st[j]&&(t==-1||dist[t]>dist[j]))
t=j;
if(i&&dist[t]==INF)return INF;//连不成最小生成树
if(i)res+=dist[t];//如果不是第一个点
st[t]=true;
for(int j=1;j<=n;j++)dist[j]=min(dist[j],g[t][j]);//dist[j]是这个点到集合的距离
}
return res;
}
int main()
{
cin>>n>>m;
memset(g,0x3f,sizeof g);
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
g[a][b]=g[b][a]=min(g[a][b],c);
sum+=g[a][b];
}
int t=prim();
cout<<sum-t;
return 0;
}
AcWing 1142. 繁忙的都市 1142
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 310,M=10010;
int n,m;
int p[N];
struct Edge
{
int a,b,w;
bool operator<(const Edge&t)const
{
return w<t.w;
}
}e[M];
int find(int x) // 并查集
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main()
{
cin>>n>>m;
for (int i = 1; i <= n; i ++ )p[i]=i;
for(int i=0;i<m;i++)
{
int a,b,w;
cin>>a>>b>>w;
e[i]={a,b,w};
}
sort(e,e+m);
int res=0;
for(int i=0;i<m;i++)
{
int a=find(e[i].a),b=find(e[i].b),w=e[i].w;
if(a!=b)
{
p[a]=b;
res=w;
}
}
cout<<n-1<<' '<<res<<endl;//n个点,n-1条边
return 0;
}
AcWing 1143. 联络员 1143
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2010,M=10010;
int n,m;
struct Edge
{
int a,b,w;
bool operator<(const Edge &t)const
{
return w<t.w;
}
}e[M];
int p[N];
int find(int x) // 并查集
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main()
{
cin>>n>>m;
for (int i = 1; i <= n; i ++ )p[i]=i;
int res=0,k=0;
for(int i=0;i<m;i++)
{
int t,a,b,w;
cin>>t>>a>>b>>w;
if(t==1)
{
res+=w;
p[find(a)]=find(b);
}
else e[k++]={a,b,w};
}
sort(e,e+k);
for(int i=0;i<k;i++)
{
int a=find(e[i].a),b=find(e[i].b),w=e[i].w;
if(a!=b)
{
p[a]=b;
res+=w;
}
}
cout<<res<<endl;
return 0;
}
AcWing 1144. 连接格点 1144
最小生成树的扩展应用
AcWing 1146. 新的开始 1146
AcWing 1145. 北极通讯网络 1145
AcWing 346. 走廊泼水节 可以
AcWing 1148. 秘密的牛奶运输 1148
负环
AcWing 904. 虫洞 904
AcWing 361. 观光奶牛 可以
AcWing 1165. 单词环 1165
差分约束
AcWing 1169. 糖果 1169
AcWing 362. 区间 可以
AcWing 1170. 排队布局 可以
AcWing 393. 雇佣收银员 可以
最近公共祖先
AcWing 1172. 祖孙询问 1172
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 40010,M=N*2;
int n,m;
int h[N], e[M], ne[M], idx;
int depth[N],fa[N][16];
int q[N];
void add(int a, int b) // 添加一条边a->b
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
void bfs(int root) // 预处理倍增数组
{
memset(depth, 0x3f, sizeof depth);
depth[0] = 0, depth[root] = 1; // depth存储节点所在层数
int hh = 0, tt = 0;
q[0] = root;
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int j = e[i];
if (depth[j] > depth[t] + 1)
{
depth[j] = depth[t] + 1;
q[ ++ tt] = j;
fa[j][0] = t; // j的第二次幂个父节点
for (int k = 1; k <= 15; k ++ )
fa[j][k] = fa[fa[j][k - 1]][k - 1];
}
}
}
}
int lca(int a, int b) // 返回a和b的最近公共祖先
{
if (depth[a] < depth[b]) swap(a, b);
for (int k = 15; k >= 0; k -- )
if (depth[fa[a][k]] >= depth[b])
a = fa[a][k];
if (a == b) return a;
for (int k = 15; k >= 0; k -- )
if (fa[a][k] != fa[b][k])
{
a = fa[a][k];
b = fa[b][k];
}
return fa[a][0];
}
int main()
{
cin>>n;
int root=0;
memset(h, -1, sizeof h);
for (int i = 0; i < n; i ++ )
{
int a,b;
cin>>a>>b;
if(b==-1)root=a;
else add(a,b),add(b,a);
}
bfs(root);
cin>>m;
while (m -- )
{
int a,b;
cin>>a>>b;
int p=lca(a,b);
if(p==a)cout<<1<<endl;
else if(p==b)cout<<2<<endl;
else cout<<0<<endl;
}
return 0;
}
AcWing 1171. 点的距离 1171
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
typedef pair<int, int> PII;
const int N = 10010, M = N * 2;
int n, m;
int h[N], e[M], w[M], ne[M], idx;
int dist[N];
int p[N];
int res[M];
int st[N];
vector<PII> query[N]; // first存查询的另外一个点,second存查询编号
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
void dfs(int u, int fa)
{
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (j == fa) continue;//说明是往上搜,停止
dist[j] = dist[u] + w[i];
dfs(j, u);
}
}
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
void tarjan(int u)
{
st[u] = 1;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (!st[j])
{
tarjan(j);
p[j] = u;//把j合并到u中
}
}
//遍历和u相关的查询
for (auto item : query[u])
{
int y = item.first, id = item.second;
if (st[y] == 2)
{
int anc = find(y);
res[id] = dist[u] + dist[y] - dist[anc] * 2;
}
}
st[u] = 2;
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
for (int i = 0; i < n - 1; i ++ )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c), add(b, a, c);
}
for (int i = 0; i < m; i ++ )
{
int a, b;
scanf("%d%d", &a, &b);
if (a != b)
{
query[a].push_back({b, i});
query[b].push_back({a, i});
}
}
for (int i = 1; i <= n; i ++ ) p[i] = i;
dfs(1, -1);//求每个点和一号点的距离
tarjan(1);
for (int i = 0; i < m; i ++ ) printf("%d\n", res[i]);
return 0;
}
AcWing 356. 次小生成树 可以
AcWing 352. 闇の 鎖 可以
有向图的强连通分量
AcWing 1174. 受欢迎的牛 2341
AcWing 367. 学校网络 可以
AcWing 1175. 最大半连通子图 添加链接描述
AcWing 368. 银河 添加链接描述
无向图的双连通分量
AcWing 395. 冗余路径 添加链接描述
AcWing 1183. 电力 添加链接描述
AcWing 396. 矿场搭建 可以
二分图
AcWing 257. 关押罪犯 可以
AcWing 372. 棋盘覆盖 可以
AcWing 376. 机器任务 可以
AcWing 378. 骑士放置 可以
AcWing 379. 捉迷藏 可以
欧拉回路和欧拉路径
AcWing 1123. 铲雪车 添加链接描述
AcWing 1184. 欧拉回路 添加链接描述
AcWing 1124. 骑马修栅栏 可以
AcWing 1185. 单词游戏 添加链接描述
拓扑排序 254人
AcWing 1191. 家谱树 可以
AcWing 1192. 奖金 添加链接描述
AcWing 164. 可达性统计 可以
AcWing 456. 车站分级 添加链接描述