【NOI2015模拟8.19】图(SPFA)

本文介绍了一种解决特定图论问题的方法:判断在给定的无向图中,两个点间是否存在通过特定数量的边可达的路径。该方法特别关注路径长度的奇偶性,并提出使用最短路径算法来解决此问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

给定一个 n 个点 m 条边的无向图,进行多次询问,每次询问点 a 是否能经过恰好 c 条边到达点 b(当然,可以对于一条边可以来回经过多次) 。

Input

第一行三个数 n,m,q,其中 q 表示询问数。
接下来 q 行,每行三个数 a,b,c。

Output

对于每次询问,如果存在一种走法,输出 TAK,否则输出 NIE

Sample Input

8 7 4
1 2
2 3
3 4
5 6
6 7
7 8
8 5
2 3 1
1 4 1
5 5 8
1 8 10

Sample Output
TAK
NIE
TAK
NIE
Data Constraint
对于 50%的数据,n<=800。
对于 100%的数据,2<=n<=5000,1<=m<=5000,1<=q<=1000000,1<=a,
b<=n,1<=c<=10^9。

The Solution

解法很显然,对于每一个询问是否存在一种走法,我们只需比较一下这个从A点走c步到达B点的c步是否大于从A点到B点的最短路径嘛。证明显然。
所以我们只需求出每一个点到达目的的最短路,就是N次SPFA。
处理一下边的奇偶性就好了。
为什么要处理奇偶性?因为假设你要走c步,而c是个奇数,那你最优的路径是偶数边,那么显然不符合题意,无解啊
那么我们就要多开两个数组,一个存走奇数边的最短路,一个存走偶数边的最短路。

假设要走2m+1步,你最短路要2n步,那么你就少走2(m-n)+1步了
注意到这里有个1,我们就开两个最短路数组计算好了。

于是就可以切了。

注意

还有一点,就是还是手打队列吧,我就是用了STL的queue导致T了80我也很无奈~~

Code

#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define fd(i,a,b) for (int i=a;i>=b;i--)
#define N 5005
#define INF 1 << 30

using namespace std;

struct Edge
{
    int to,next;
    Edge(void){}
    Edge(int a,int b) : to(a),next(b){}
}E[N * 2];

struct node
{
    int Odd,Eve;
}Dis[N][N];

//queue <int> Q;

int Final[N],tot = 0,n,m,q,d[N * 5];
bool Flag[N];

int read(int &n)
{
    char ch = ' ';
    int q = 0, w = 1;
    for (;(ch != '-') && ((ch < '0') || (ch> '9'));ch = getchar());
    if (ch == '-') w = -1,ch = getchar();
    for (; ch >= '0' && ch <= '9';ch = getchar()) q = q * 10 + ch - 48;
    n = q * w;
    return n;
}

void Link(int x,int y)
{
    E[++ tot] = Edge(y,Final[x]),Final[x] = tot;
    E[++ tot] = Edge(x,Final[y]),Final[y] = tot;
}

void Spfa(int x,int id)
{
    memset(Flag,false,sizeof(Flag));
    //Q.push(x);
    Flag[x] = true;
    fo(i,1,n) Dis[id][i].Odd = Dis[id][i].Eve = INF;
    Dis[id][x].Eve = 0;
    int l = 0,r = 1;
    d[1] = x;
    //while (! Q.empty())
    while (l < r)
    {
        int now = d[++ l];
        //int now = Q.front();
        //Q.pop();
        Flag[now] = false;
        for (int k = Final[now];k;k = E[k].next)
        {

            if (Dis[id][now].Odd + 1 < Dis[id][E[k].to].Eve)
            {
                Dis[id][E[k].to].Eve = Dis[id][now].Odd + 1;
                if (! Flag[E[k].to])
                {
                    Flag[E[k].to] = true;
                    //Q.push(E[k].to);  
                    d[++ r] = E[k].to;
                }   
            }
            if (Dis[id][now].Eve + 1 < Dis[id][E[k].to].Odd)
            {
                Dis[id][E[k].to].Odd = Dis[id][now].Eve + 1;
                if (! Flag[E[k].to])
                {
                    Flag[E[k].to] = true;
                    //Q.push(E[k].to);  
                    d[++ r] = E[k].to;
                }   
            }

        }   
    }
} 

int main() 
{
    freopen("xor4.in","r",stdin);
    freopen("data.out","w",stdout);
    read(n),read(m),read(q);
    fo(i,1,m) 
    {
        int x,y;
        read(x),read(y);
        Link(x,y);  
    }   
    fo(i,1,n) Spfa(i,i);
    while (q --)
    {
        int x,y,z;
        read(x),read(y),read(z);
        if (x == y && Final[x] == 0) {printf("NIE\n");continue;} 
        if (z & 1)
        {
            if (z >= Dis[x][y].Odd) printf("TAK\n");
            else printf("NIE\n");
        }
        else
        {
            if (z >= Dis[x][y].Eve) printf("TAK\n");
            else printf("NIE\n");
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值