• 【NOIP训练】【Tarjan求割边】上学


    题目描述

    给你一张图,询问当删去某一条边时,起点到终点最短路是否改变。

    输入格式

    第一行输入两个正整数n,m,分别表示点数和边数。
    第二行输入两个正整数S,T,起点标号为S,终点标号为T
    接下来m行,每行三个整数x,y,z,表示有一条连接x,y的道路,长度为z
    接下来一个整数Q,表示询问的个数。
    最后Q行,每行一个正整数x,表示询问若删去第x条边,ST最短路是否改变。

    输出格式

    输出Q行。
    对于每一个询问,ST最短路没有改变则输出一行一个字符串,否则输出.

    输入样例

    8 11

    1 8

    1 2 3

    1 3 1

    2 3 1

    2 4 5

    2 5 1

    4 5 4

    3 5 2

    5 6 4

    6 7 5

    6 8 2

    7 8 5

    5

    2

    3

    8

    4

    10

    输出样例

    No
    Yes
    No
    Yes
    No

    数据范围

    1$leq$Q,N$leq$40000,M$leq$200000,保证源点到任意点的最短路长度不超过10^9

     

    题解

    找出 ST 最短路的幅副图,最短路有改变仅当删去的一条边为割边,在副图上Tarjan求割边即可。

     

     

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    
    int n,m,s,t,q,tot,dex;
    int last[40005];
    int dis,dis1[40005],dis2[40005];
    int flag[500005],dfn[40005],low[40005];
    struct hh
    {int l,r,w;}line[500005];
    struct hhh
    {int next,fr,to,w;}e[500005];
    void add(int a,int b,int w)
    {
        e[++tot].to=b;
        e[tot].fr=a;
        e[tot].w=w;
        e[tot].next=last[a];
        last[a]=tot;
    }
    void spfa1()
    {
        int i,j,now;
        bool inq[40005];
        memset(inq,0,sizeof(inq));
        for(i=1;i<=n;i++) dis1[i]=999999999;
        queue<int> q;
        q.push(s);dis1[s]=0;
        while(!q.empty())
        {
            now=q.front();q.pop();inq[now]=false;
            for(i=last[now];i;i=e[i].next)
            if(dis1[now]+e[i].w<dis1[e[i].to])
            {
                dis1[e[i].to]=e[i].w+dis1[now];
                if(!inq[e[i].to])
                {
                    inq[e[i].to]=true;
                    q.push(e[i].to);
                }
            }
        }
    }
    void spfa2()
    {
        int i,j,now;
        bool inq[40005];
        memset(inq,0,sizeof(inq));
        for(i=1;i<=n;i++) dis2[i]=999999999;
        queue<int> q;
        q.push(t);dis2[t]=0;
        while(!q.empty())
        {
            now=q.front();q.pop();inq[now]=false;
            for(i=last[now];i;i=e[i].next)
            if(dis2[now]+e[i].w<dis2[e[i].to])
            {
                dis2[e[i].to]=e[i].w+dis2[now];
                if(!inq[e[i].to])
                {
                    inq[e[i].to]=true;
                    q.push(e[i].to);
                }
            }
        }
    }
    
    void tarjan(int now,int fa)
    {
        int i,j;
        low[now]=dfn[now]=++dex;
        for(i=last[now];i;i=e[i].next)
        if(e[i].to!=fa)
        {
            if(dfn[e[i].to]) low[now]=min(low[now],dfn[e[i].to]);
            else
            {
                tarjan(e[i].to,now);
                low[now]=min(low[now],low[e[i].to]);
                if(low[e[i].to]>dfn[now]) flag[e[i].w]=true;
            }
        }
    }
    int main()
    {
        int i,j,u,v,w,x;
        scanf("%d%d%d%d",&n,&m,&s,&t);
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            line[i].l=u;line[i].r=v;line[i].w=w;
            add(u,v,w);add(v,u,w);
        }
        spfa1();spfa2();dis=dis1[t];
        memset(last,0,sizeof(last));tot=0;
        for(i=1;i<=m;i++)
        {
            u=line[i].l;v=line[i].r;w=line[i].w;
            if(dis1[u]+w+dis2[v]==dis||dis1[v]+w+dis2[u]==dis)
            add(u,v,i),add(v,u,i);
        }
        tarjan(s,0);
        scanf("%d",&q);
        for(i=1;i<=q;i++)
        {
            scanf("%d",&x);
            if(flag[x]&&x) puts("No");
            else puts("Yes");
        }
        return 0;
    
    }

     

  • 相关阅读:
    细菌 状态压缩
    素数
    骑士问题(knight)
    魔法石的诱惑
    平面上的最接近点对
    救援行动(save)
    优先队列
    leetcode 92. 反转链表 II
    leetcode 91. 解码方法
    leetcode 39. 组合总和
  • 原文地址:https://www.cnblogs.com/yljiang/p/6057332.html
Copyright © 2020-2023  润新知