acwing算法提高课第三章图论

第三章 图论

包括单源最短路的建图方式、单源最短路的综合应用、单源最短路的扩展应用、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. 车站分级 添加链接描述

### 回答1: acwing算法基础是一门针对算法学习的在线程,在这门程中,学生可以系统地学习和掌握算法基础知识,提高编程水平。为了方便学生学习,acwing提供了网盘服务。 acwing算法基础网盘是一个用于存储程资源的平台。通过这个网盘,学生可以下载程讲义、代码模板以及补充材料等。这些资源都经过精心整理,供学生们参考和学习。 网盘中的资源是按照程章节进行分类的,学生可以根据自己的学习需要,选择性地下载所需的资料。同时,网盘还提供了搜索功能,方便学生快速定位和获取所需资料。 acwing算法基础网盘的使用对于学生们的学习非常有帮助。通过下载和学习这些资源,学生们可以更好地理解程内容,加深对算法的理解。此外,学生们还可以通过研究代码模板,学习优秀的编程思想和技巧,提高自己的编程能力。 总之,acwing算法基础网盘是一项非常便利和实用的服务,为学生们提供了更加全面和深入的学习资源,帮助他们更好地掌握和运用算法知识。 ### 回答2: acwing算法基础是一门优质的算法学习资源,其中的程内容丰富多样,涵盖了算法基础知识、数据结构、动态规划、图论等等。很多学习者都认为这门程对他们的算法学习有很大的帮助。 网盘是指以网络为媒介,提供文件存储和下载服务的云存储平台。acwing算法基础也提供了网盘服务,方便学习者下载程资料并进行学习。 通过acwing算法基础网盘,学习者可以方便地获取到程的各种学习资料,包括讲义、习题集、代码示例等。这些资料可以帮助学习者更好地理解和掌握程的内容。此外,网盘还提供了上传和分享功能,学习者可以将自己的学习心得、代码等资料分享给其他学习者,促进学习者之间的互相学习和交流。 acwing算法基础网盘的优点不仅仅是方便快捷的下载和分享功能,还包括安全可靠的存储环境。学习者可以放心地将自己的学习资料上传到网盘进行备份,减少数据丢失的风险。同时,网盘还提供了多种存储空间容量的选择,满足学习者不同的需求。 总的来说,acwing算法基础网盘为学习者提供了方便、安全和多样化的学习资源下载和分享服务,为学习者的算法学习和进步提供了有力的支持。如果你对算法感兴趣,我推荐你去尝试一下这门精彩的程!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值