【JZOJ4597】现世斩

来源:互联网 发布:在哪看淘宝自动售货 编辑:程序博客网 时间:2024/06/10 10:55

Description

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

Solution

跟GDOI2016第二天的第一题好像啊!
不过我换了一种方法。
先从1到n做一遍dij,然后再从n到1做一遍dij。
每次枚举一个中间点x,然后求出min{d[1,u]}和min{d[v,n]},u和v与x直接相连,最后用d[1,u]+d[v,n]+2来更新最优答案就行。复杂度O(nm)。

Code

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int maxn=100005,maxm=1000005;typedef long long LL;int n,m,tot,h[maxn],e[maxm],next[maxm],len[maxm],pos[maxn],id[maxn];LL f1[maxn],f2[maxn],ans;char c;int read(){    for (c=getchar();c<'0' || c>'9';c=getchar());    int x=c-48;    for (c=getchar();c>='0' && c<='9';c=getchar()) x=x*10+c-48;    return x;}void add(int x,int y,int l){    e[++tot]=y; next[tot]=h[x]; len[tot]=l; h[x]=tot;}void Up(int x,LL *f){    while (x>1 && f[id[x/2]]>f[id[x]])    {        id[x/2]^=id[x]^=id[x/2]^=id[x];        pos[id[x]]=x;        x/=2;    }    pos[id[x]]=x;}void Down(int x,LL *f){    while (x*2<=tot)    {        int son=(x*2==tot || f[id[x*2]]<f[id[x*2+1]])?x*2:x*2+1;        if (f[id[x]]<=f[id[son]]) break;        id[x]^=id[son]^=id[x]^=id[son];        pos[id[x]]=x;        x=son;    }    pos[id[x]]=x;}void Dij(int x,LL *f){    for (int i=1;i<=n;i++) f[i]=1e16;    f[x]=0;    tot=1;    id[1]=x; pos[x]=1;    for (int i=1;i<=n;i++) if (i!=x)    {        id[++tot]=i; pos[i]=tot;    }    for (int i=1;i<=n;i++)    {        int x=id[1];        id[1]=id[tot--]; pos[id[1]]=1;        Down(1,f);        for (int j=h[x];j;j=next[j]) if (f[x]+len[j]<f[e[j]])        {            f[e[j]]=f[x]+len[j]; Up(pos[e[j]],f);        }    }}int main(){    freopen("cut.in","r",stdin);     freopen("cut.out","w",stdout);    n=read(); m=read();    while (m--)    {        int x=read(),y=read(),l=read();        add(x,y,l); add(y,x,l);    }    Dij(1,f1); Dij(n,f2);    ans=f1[0]=f2[0]=1e16;    for (int i=h[1];i;i=next[i]) ans=min(ans,f2[e[i]]+1);    for (int i=h[n];i;i=next[i]) ans=min(ans,f1[e[i]]+1);    for (int i=2;i<n;i++)    {        int x=0,y=0;        for (int j=h[i];j;j=next[j])        {            if (f1[e[j]]<f1[x]) x=e[j];            if (f2[e[j]]<f2[y]) y=e[j];        }        ans=min(ans,f1[x]+f2[y]+2);    }    printf("%lld\n",ans);    fclose(stdin); fclose(stdout);    return 0;}
1 0
原创粉丝点击