JZOJ4597. 现世斩 题解

题目

Description

异变又发生了,魂魄妖梦作为幻想乡的一名自(cheng)机(guan),主动前去解决异变。
我们用一个n个点、m条边的无向联通图来表示妖梦可选择的路线,妖梦从白玉楼出发,白玉楼被视为编号为1的点,编号为2——n的点是幻想乡的村庄,其中编号为n的村庄发生了异变。
每条边上可能会有一些妖怪袭击人类(然而妖梦是半人半灵),所以对于第i条边,妖梦需要t[i]分钟通过这条路。妖梦带了她的人符[现世斩],可以使所有连接点x的边的通过时间变成1(x可以任意指定)。然而为了保留足够的力量解决异变,妖梦只会用这个符卡一次。妖梦想知道,她到达村庄n的最短时间是多少。

Input

第一行两个整数n,m。接下来m行,每行三个整数u[i],v[i],t[i],表示这条无向边连接点u[i],v[i],通过时间为t[i]。

Output

一个整数,表示妖梦到达村庄的最短时间。

Sample Input

5 5
1 2 3
2 3 3
3 5 2
2 4 1
4 5 4

Sample Output

4

Data Constraint

40%:n≤1000 m≤5000
100%:2≤n≤100,000 n-1≤m≤500,000 1≤t[i]≤1,000,000,000
可能有重边和自环,保证能从1到n

Hint

当x=1,最短路径长度为6:1->2->3->5或1->2->4->5
当x=2,最短路径长度为4:1->2->3->5
当x=3,最短路径长度为5:1->2->3->5
当x=4,最短路径长度为5:1->2->4->5
当x=5,最短路径长度为5:1->2->4->5

分析

这题让我们想到了GDOI2016的SigemaGO

我们知道修改过后的X一定是在答案的最短路上面的,也就是说,修改这个X的影响是:
原来这条路径上面上,连接某个点的两条边的权值变为1。

现在题目就变为:如果有一个三元组a,b,c,a到b有一条有向边,b到c有一条有向边,
若对点b使用一次【现世斩】,就可以是得a到c之间产生一条权值为2的有向边。

解法

现在就可以用分层SPFA来解决。
我们先把图分成三层,第一层是原图,第二层是用来衔接第一、第三层,第三层跟第一层一样。

层与层之间的边权为1。

code

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#define ll long long 
using namespace std;

struct arr{
    int x,y;
    ll z;
}aa[500003];

int a[4000003],next[4000003];
ll v[4000003];
int b[300003];
ll dis[900003];
int n,m,x,y,tot;
ll z;
int data[8000003];
bool bz[300003];

void read(int &n)
{
    char ch=getchar();
    while(ch==' ' || ch=='\n') ch=getchar();int q=0;
    while(ch>='0' && ch<='9' ) {q=q*10+ch-48;ch=getchar( );}n=q;
}

void read1(ll &n)
{
    char ch=getchar();
    while(ch==' ' || ch=='\n') ch=getchar();ll q=0;
    while(ch>='0' && ch<='9' ) {q=q*10+ch-48;ch=getchar( );}n=q;
}

void add(int x,int y,ll z)
{
    tot++;
    next[tot]=b[x];
    a[tot]=y;
    v[tot]=z;
    b[x]=tot;
}

bool cmp(arr x,arr y)
{
    return((x.x<y.x)||((x.x==y.x)&&(x.y<y.y))||((x.x==y.x)&&(x.y==y.y)&&(x.z<y.z)));
}

void spfa(int x)
{
    memset(dis,127,sizeof(dis));
    memset(bz,1,sizeof(bz)); 
    int i=0,j=1;
    dis[x]=0;
    data[1]=x;
    bz[x]=0;
    while(i<j)
    {
        i++;
        for(int k=b[data[i]];k;k=next[k])
        {
            if(dis[data[i]]+v[k]<dis[a[k]])
            {
                dis[a[k]]=dis[data[i]]+v[k];
                if(bz[a[k]])
                {
                    bz[a[k]]=0;
                    j++;
                    data[j]=a[k];
                }
            }
        }
        bz[data[i]]=1;
    }
}
int main()
{
    freopen("cut.in","r",stdin);
    freopen("cut.out","w",stdout);
    //scanf("%d%d",&n,&m);
    read(n);
    read(m); 
    for(int i=1;i<=m;i++)
    {
        //scanf("%d%d%lld",&aa[i].x,&aa[i].y,&aa[i].z);
        read(aa[i].x);
        read(aa[i].y);
        read1(aa[i].z);
    }
    sort(aa+1,aa+1+m,cmp); 
    for(int i=1;i<=m;i++)
        if((aa[i].x!=aa[i-1].x)||((aa[i].y!=aa[i-1].y)))
        {
            add(aa[i].x,aa[i].y,aa[i].z);
            add(aa[i].y,aa[i].x,aa[i].z);
            add(n+n+aa[i].x,n+n+aa[i].y,aa[i].z);
            add(n+n+aa[i].y,n+n+aa[i].x,aa[i].z);
            add(aa[i].x,aa[i].y+n,1);
            add(aa[i].y,aa[i].x+n,1);
            add(aa[i].x+n,aa[i].y+n+n,1);
            add(aa[i].y+n,aa[i].x+n+n,1);
        }
    spfa(1);
    ll ans=min(dis[n],dis[n+n]);
    ans=min(ans,dis[n+n+n]);
    spfa(1+n);
    printf("%lld\n",min(ans,dis[n+n+n]));
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值