禁地

裸的最短路(SPFA)+最小生成树(kruskal)
注意:最短路和最小生成树要分开存图
题目描述
ix 和木易来到了 loi 门派的禁地之所。但是因为有复杂的阵法禁制他们无法进入, 于是在禁制外 开始思考。
他们看到, 阵法是由 n 个从 1 到 n 标号的岛屿组成的, 岛屿之间由念力光线连接, 因为 ix 和木 易拥有云中靴所以他们可以沿着念力光线通行, 即两个岛屿间只要有一条念力光线的存在, 那么 他们就可以自由往返于两个岛屿间, 前提是他们的起始点要在其中的任意一个岛屿上。他们两人在阵法里不断穿梭游动着……渐渐的忘记了他们来时的目的。
他们发现念力光线是有长度的, 于是 ix 想要计算一下连接阵法内各个岛屿使得各个岛屿都互相联 通的最短光线长度, 但木易想要计算一下从他们现在所站的位置到阵法内任意一个岛屿的最短路 径长度。 两人发生了争执, 因为声音过大激发了学长大神们留下的惩罚禁制之灵。
禁制之灵很不客气的让他们在 2s 之内计算出他们各自的问题, 但是ix 和木易都是学过 oi 的所以 很轻松的就切掉了, 禁制之灵便增大了难度, 它会多次询问并会在某两个岛屿之间添加一条念力 光线。
因为 Ix 和木易实在是太弱了, 都不会做, 于是找到了学信息奥赛的你, 请你帮他们解决这个问 题
输入格式
输入文件的第一行为两个正数 n, m 代表原阵法内的岛屿总数和念力光线总数。
以下 m 行, 每行三个正数 u, v, l. 代表每条念力光线连接的两个点以及其长度。
再一行只有一个正数 q, 代表操作数。
接下来每行先是一个正数 p, 若 p 为 1 则后面跟着两个正数 s, t。 请输出从 s 岛屿到 t 岛屿的最短 路径长度;若 p 为 2, 则输出连接阵法内各个岛屿使得各个岛屿都互相联通的最短光线长度; 若 p 为 3, 则后跟三个正数 a, b, d, 代表在 a, b 之间添加一条长度为 d 念力光线。
输出格式
与输入文件操作中 1, 2 操作数对应。
输入样例
5 8
1 2 4
2 3 6
3 4 2
4 5 6
1 3 2
1 3 8
2 5 3
4 1 5
5
1 1 5
2
3 1 5 1
1 1 5
2
输出样例
7 11 1 8
数据说明
0 <光线的长度<= 1000
n<=10000,m<=100000

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
queue<int>q;
int n,m;
const int maxn=10000+5,maxm=100000+5;
bool exi[maxn];
long long dis[maxn];
int first[maxn],next[maxm<<1],tot=0,num=0;
int fa[maxn],rank[maxn];
struct edge
{
    int f,t,v;
}es[maxm<<1],ess[maxm<<1];
bool cmp(edge ha,edge ho)
{
    return ha.v<ho.v;
}
int find(int x)
{
    if(fa[x]==x) return x;
    return fa[x]=find(fa[x]);
}
int merge(int x,int y)
{
    if(rank[x]==rank[y])
    {
        fa[x]=y;
        rank[y]++;
    }
    else if(rank[x]>rank[y])
    {
        fa[y]=x;
    }
    else fa[x]=y;
}
void build(int f,int t,int v )
{
    es[++tot]=(edge){f,t,v};
    next[tot]=first[f];
    first[f]=tot;
}
int spfa(int s,int t)
{
    memset(dis,63,sizeof(dis));
    memset(exi,0,sizeof(exi));
    while(!q.empty())
    {
        q.pop();
    }
    dis[s]=0;
    q.push(s);
    exi[s]=1;

    while(!q.empty())
    {
        int top=q.front();
        exi[top]=0;
        q.pop();
        for(int i=first[top];i;i=next[i])
        {
            if(dis[es[i].t]>dis[top]+es[i].v)
            {
              dis[es[i].t]=dis[top]+es[i].v;
              if(!exi[es[i].t]) 
              {

                q.push(es[i].t);
                exi[es[i].t]=1;
              } 
            } 
        }
    }
    return dis[t];
}
int kruskal()
{
    int times=0;
    long long ans=0;
    for(int i=1;i<=n;i++)
      fa[i]=i;
    sort(ess+1,ess+1+num,cmp);
    for(int i=1;i<=num;i++)
    {
    int x=find(ess[i].f);
    int y=find(ess[i].t);
    if(x!=y)
     {
        merge(x,y);
        ans+=ess[i].v;
        ++times;
     }
    if(times==n-1) break;  
    }
    return ans;

}
int main()
{
    int a,b,c;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        ess[++num]=(edge){a,b,c};
        build(a,b,c);
        build(b,a,c);
    }
    int p;
    scanf("%d",&p);
    for(int i=1;i<=p;i++)
    {
        scanf("%d",&a);
        if(a==1)
        {
            scanf("%d%d",&b,&c);

            printf("%lld",spfa(b,c));
        }
        else if(a==2)
        {
            printf("%lld",kruskal());
        }
        else if(a==3)
        {
            int d;
            scanf("%d%d%d",&d,&b,&c);
            build(d,b,c);
            build(b,d,c);
            ess[++num]=(edge){d,b,c};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值