• [BZOJ4289] [PA2012] Tax 解题报告 (最短路+差分建图)


    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4289

    4289: PA2012 Tax

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 1029  Solved: 310
    [Submit][Status][Discuss]

    Description

    给出一个N个点M条边的无向图,经过一个点的代价是进入和离开这个点的两条边的边权的较大值,求从起点1到点N的最小代价。起点的代价是离开起点的边的边权,终点的代价是进入终点的边的边权
    N<=100000
    M<=200000

    Sample Input

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

    Sample Output

    12

                                 [Submit][Status][Discuss]
     

    比较有技巧的建图

    首先考虑暴力点的建图:

    把每条无向边拆成两条有向边.把每条边看成一个点,对于两条边a->b,b->c

    在这两条边之间连有向边,边权为这两条边的权值的较大值.

    新建源点S,汇点T, S向所有从1连出去的边连边,所有指向n的边向T连边. 求S->T的最短路即可.

    这样的复杂度会达到O(m2)

    考虑优化一下:

    考虑利用差值来建边.

    依然把每条边x-y拆成x->y,y->x.

    枚举每个中转点x. 将x的出边按权值排序,x的每条入边向对应的出边连该边权值的边(i对应i^1)),x的每条出边向第一个比它大的出边连两边权差值的边,x的每条出边向第一个比它小的出边连权值为0的边. 新建源汇S,T S向每条1的出边连权值为该边边权的边.每条n的入边向T连该边权值的边.

     

    仔细看图,L2>L1,发现从S到T的路径走的一定是L2,而不是L1;注意结合上述的建图过程(红色边)

    跑S->T的最短路即可.

    这样的复杂度是O(mlogm)就可以AC

    我的SPFA被T了,估计没写错只是被卡了

    以上内容部分参考DaD3zZ大佬的博客

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    #define ll long long
    using namespace std;
    
    const int N=200000+15;
    int n,m,tot1,tot2,S,T;
    int head[N],h[N<<1],st[N<<1],vis[N<<1];
    ll dist[N<<1];
    struct EDGE
    {
        int to;int next;int l;
    }edge[N<<1],e[N<<3];
    inline int read()
    {
        char ch=getchar();
        int s=0,f=1;
        while (!(ch>='0'&&ch<='9')) {if (ch=='-') f=-1;ch=getchar();}
        while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
        return s*f;
    }
    void init()
    {
        memset(head,-1,sizeof(head));
        memset(h,-1,sizeof(h));
        memset(dist,0x3f3f3f3f,sizeof(dist));
    }
    void addedge(int x,int y,int l)
    {
        edge[tot1]=(EDGE){y,head[x],l};
        head[x]=tot1++;
    }
    bool cmp(int x,int y) {return edge[x].l<edge[y].l;}
    void addroad(int x,int y,int l)
    {
        e[tot2]=(EDGE){y,h[x],l};
        h[x]=tot2++;
    }
    void build()
    {
        S=2*(m+1)+15;T=2*(m+1)+16;
        for (int i=1;i<=n;i++)
        {
            int tp=0;
            for (int j=head[i];j!=-1;j=edge[j].next)
                st[++tp]=j;
            sort(st+1,st+1+tp,cmp);
            for (int j=1;j<=tp;j++)
            {
                int now=st[j],suc=st[j+1];    
                if (edge[now].to==n) addroad(now,T,edge[now].l);
                if (i==1) addroad(S,now,edge[now].l);
                addroad(now^1,now,edge[now].l);
                if (j<tp) {addroad(now,suc,edge[suc].l-edge[now].l);addroad(suc,now,0);}
            }
        }
    }
    priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > > q;
    void Dijkstra()
    {
        q.push(make_pair(0,S)); 
        dist[S]=0;
        while (!q.empty())
        {
            int now=q.top().second;
            ll Dis=q.top().first;q.pop();
            if (Dis>dist[now]) continue;
            for (int i=h[now];i!=-1;i=e[i].next)
                if (dist[now]+e[i].l<dist[e[i].to])
                {
                    dist[e[i].to]=dist[now]+e[i].l;
                    q.push(make_pair(dist[e[i].to],e[i].to));
                }
        }
    }
    int main()
    {
        n=read();m=read();
        init();
        for (int i=1;i<=m;i++)
        {
            int x=read(),y=read(),l=read();
            addedge(x,y,l);addedge(y,x,l);
        }
        build();
        Dijkstra();
        printf("%lld
    ",dist[T]);
        return 0;
    }
  • 相关阅读:
    深度学习代码注解(一)—— mnistdeepauto
    只属于你我的共同记忆
    只属于你我的共同记忆
    道教的认识
    道教的认识
    作家、文学大家、大师的艺术风格
    作家、文学大家、大师的艺术风格
    视频、画面、语言、文字与脑海、心灵
    视频、画面、语言、文字与脑海、心灵
    URAL 1963 Kite 四边形求对称轴数
  • 原文地址:https://www.cnblogs.com/xxzh/p/9365963.html
Copyright © 2020-2023  润新知