SPOJ:Terrorists(LCA + SPFA最短路)

AR2015PC - Terrorists

no tags 

Terrorists! There are terrorists everywhere!!! I was shouting out loud after I had watched a few news
reports about terrorism acts that had happened around my neighborhoods. I started to think that hiding at
home wasn’t a good way to go.
I went to police stations and asked around if I could help them prevent these issues. The police
gave me pieces of information about terrorists’ plans. I ended up lying on my bed and figuring way to
utilize this information. That is why I come to you.
Our neighborhoods could be illustrated with intersections and roads. Terrorists usually meet up at
one intersection before moving to another intersection to perform an illegal act. The given information
tells us where they will meet and where they will go. Unfortunately, the certain schedules of those plans
are not available and too few policemen are available lately. So, the police will not be able to set up
efficient defenses to all of those acts. What they could do is set up surveillance cameras to all meet up
intersections. Once a meet up is detected, policemen will go to that meet up’s destination to catch the
terrorists. The policemen rarely accomplish it because they spend too long time travelling between places.
Because terrorists are smart, they always use shortest route to travel between intersections.
Because the policemen aren’t that smart, they need our help. For each terrorist plan, the police want us to
compute the shortest distance between the meet up place and the destination. If the distance is too short,
they will not spend their efforts for free.
Input
The first line of the input contains an integer T, the number of test sets (1 <= T <= 5).
Each test case consists of many lines. The first line contains 3 integers N, M, Q which are the
number of intersections, the number of roads, and the number of terrorists’ plans in respective order.
1 <= N <= 100 000, N - 1 <= M <= N + 50, 1 <= Q <= 50 000
Then the next M lines describe the roads in our neighborhoods. A road is described by 3
integers: U, V, DUi and Vi represent 2 ends of the road iDi represents distance of that road.
1 <= Ui, Vi <= N, 1 <= Di <= 10 000. It is possible that many roads might exist between a pair of
intersection.
Finally the next Q lines are the terrorism plans. A plan j is described by 2 integers: Sj and Ej
which are the meet-up intersection and the destination intersection respectively. 1 <= Sj, Ej <= N.
Output
For each test set, print out “Case x:” where x is a case number beginning with 1, and then
followed by Q lines. For each terrorism plan, output the shortest distance between the meet-up
intersection and the destination of the plan.

Example

Input:
1
7 9 5
7 6 6114
5 3 5473
2 4 2601
5 4 8525
7 3 291
2 7 3363
1 6 399
6 4 4477
1 7 3075
6 3
4 4
3 1
2 3
7 3
Output: Case 1:
3765
0
3366
3654
291

题意:给n个点,m条边的无向图,Q个询问,输出两点间的最短距离。

思路:观察m的范围,显然该图可以是一棵树加上多余的边组成,所以先构造一颗生成树,可用LCA+Tarjan离线较快地求出所有点的距离,对于多余的边(最多50条),这些边的端点去重后全部跑一遍SPFA即可,下面代码采用前向星存边。

# include <iostream>
# include <cstdio>
# include <cstring>
# include <queue>
# include <algorithm>
# define maxn 100100
# define ll long long
using namespace std;
int n, m, q, cnt, cnt1, cnt2, cnt3, E[maxn][3], dis[maxn], vis[maxn], vis1[maxn];
int ex_node[maxn<<1], next0[maxn], next1[maxn], next2[maxn], pre[maxn], ex_dis[110][maxn];
queue<int>Q;
struct node
{
    int e, w, next;
}edge[maxn<<1],all_edge[maxn<<1];

struct node2
{
    int e, next, anc;
}query[maxn];

void init(int n)
{
    cnt = cnt1 = cnt2 = cnt3 = 0;
    for(int i=0; i<=n; ++i)
        pre[i] = i;
    memset(next0, -1, sizeof(next0));
    memset(next1, -1, sizeof(next1));
    memset(next2, -1, sizeof(next2));
    memset(vis, 0, sizeof(vis));
    memset(dis, 0, sizeof(dis));
}

int find(int x)
{
    if(x != pre[x])
        pre[x] = find(pre[x]);
    return pre[x];
}

void uni(int a, int b)
{
    int x = find(a);
    int y = find(b);
    if(x != y)
        pre[y] = x;
}

void add_edge(int u, int v, int w)//用于计算LCA。
{
    edge[cnt].e = v;
    edge[cnt].w = w;
    edge[cnt].next = next0[u];
    next0[u] = cnt++;
}

void add_all_edge(int u, int v, int w)//所有边,用于计算SPFA。
{
    all_edge[cnt1].e = v;
    all_edge[cnt1].w = w;
    all_edge[cnt1].next = next1[u];
    next1[u] = cnt1++;
}

void add_query(int u, int v)//所有询问。
{
    query[cnt2].e = v;
    query[cnt2].next = next2[u];
    next2[u] = cnt2++;
}

void lca(int u)
{
    vis[u] = 1;
    for(int i=next0[u]; i!=-1; i=edge[i].next)
    {
        int v = edge[i].e;
        int w = edge[i].w;
        if(vis[v]) continue;
        dis[v] = dis[u] + w;
        lca(v);
        uni(u, v);
    }
    for(int i=next2[u]; i!=-1; i=query[i].next)
    {
        int v = query[i].e;
        if(vis[v])
            query[i].anc = query[i^1].anc = find(v);
    }
}

void spfa(int head, int *ex_dis)//最短路。
{
    while(!Q.empty()) Q.pop();
    memset(vis1, 0, sizeof(vis1));
    for(int i=0; i<=n; ++i)
        ex_dis[i] = 0x3f3f3f3f;
    vis1[head] = 1;
    ex_dis[head] = 0;
    Q.push(head);
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        vis1[u] = 0;
        for(int i=next1[u]; i!=-1; i=all_edge[i].next)
        {
            int v = all_edge[i].e;
            int w = all_edge[i].w;
            if(ex_dis[v] > ex_dis[u] + w)
            {
                ex_dis[v] = ex_dis[u] + w;
                if(!vis1[v])
                {
                    vis1[v] = 1;
                    Q.push(v);
                }
            }
        }
    }
}

void build()//构造生成树,用并查集找出多余的边。
{
    for(int i=0; i<m; ++i)
    {
        int u = E[i][0];
        int v = E[i][1];
        int w = E[i][2];
        int x = find(u);
        int y = find(v);
        if(x != y)
        {
            pre[y] = x;
            vis[i] = 1;
            add_edge(u, v, w);
            add_edge(v, u, w);
        }
    }
}

int main()
{
    int t, a, b, c, cas=1;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&n,&m,&q);
        init(n);
        for(int i=0; i<m; ++i)
        {
            scanf("%d%d%d",&E[i][0],&E[i][1],&E[i][2]);
            add_all_edge(E[i][0], E[i][1], E[i][2]);
            add_all_edge(E[i][1], E[i][0], E[i][2]);
        }
        build();
        for(int i=0; i<q; ++i)
        {
            scanf("%d%d",&a,&b);
            add_query(a, b);
            add_query(b, a);
        }
        for(int i=0; i<m; ++i)
        {
            if(!vis[i])
            {
                ex_node[cnt3++] = E[i][0];
                ex_node[cnt3++] = E[i][1];
            }
        }
        memset(vis, 0, sizeof(vis));
        for(int i=0; i<=n; ++i)
            pre[i] = i;
        lca(1);
        sort(ex_node, ex_node+cnt3);
        cnt3 = unique(ex_node, ex_node+cnt3) - ex_node;
        for(int i=0; i<cnt3; ++i)
            spfa(ex_node[i], ex_dis[i]);
        printf("Case %d:\n",cas++);
        for(int i=0; i<q; ++i)
        {
            int u = query[i<<1].e;
            int v = query[i<<1|1].e;
            int anc = query[i<<1].anc;
            ll ans = (ll)ex_dis[0][u] + (ll)ex_dis[0][v];
            for(int j=1; j<cnt3; ++j)
                ans = min(ans, (ll)ex_dis[j][u] + (ll)ex_dis[j][v]);//必经过这些点。
            ans = min(ans, (ll)dis[u]+(ll)dis[v] - ((ll)dis[anc]<<1));//仅经过生成树。
            printf("%lld\n",ans);
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值