[PA2012]Tax 题解

Statement

[PA2012]Tax - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

Solution

dijkstra+最短路建模

题目的难点显然在于说怎么处理这个最大值的问题

嗯...先把一条双向边拆成两条有向边...权值在边不好搞,考虑边转点?...这个最大值有点像带悔贪心...

对于一个点,我们把与它有关的边分为入边和出边

(上图中 \(in,out\) 是对应的,因为原图是无向图)

先对边转成的点按照边权排序

考虑到最大值这个带悔贪心类似的东西,我们可以想到像这样连边

其中 \(\Delta w\) 表示的是两条边边权差。

这样连边之后,我们从 \(in_1\) 这边过来就有了选择的余地,比如想从 \(out_2\) 出去,会得到正确贡献

当我们从 \(in_2\) 过来的时候,因为取 \(max\) ,所以 \((out_2,out_1)\) 边权 \(0\) ,很妙

最后一个问题在于处理起点和终点,

非常好办,建立超级源点 \(s\) 和超级汇点 \(t\) , 然后所有从 \(1\) 出发的边,从 \(s\) 向这些边转成的点连原图权值的边

同样,所有结束于 \(n\) 的边,从这些边转的点向 \(t\) 连原图权值的边

跑一边 \(dijkstra\) 即可,这样连边,点数是 \(O(m)\) 的,边数是 \(O(m)\) 的,复杂度 \(O(m\log m)\)

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5+5;

int read(){
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
    return s*w;
}

struct Edge{
    int nex,to;
    int dis;
}edge[N<<3];
struct Line{
    int v,w,id;
    Line(int v,int w,int id):v(v),w(w),id(id){}
    bool operator<(const Line&rhs)const{return w<rhs.w;}
};
struct node{
    int v,w;
    node(int v,int w):v(v),w(w){}
    bool operator<(const node&rhs)const{return w>rhs.w;}
};
priority_queue<node>q;
vector<Line>Edge[N];
int head[N<<2];
int dis[N<<2];
int n,m,elen=1,s=0,t=1;
bool vis[N<<2];

bool chkmin(int &a,int b){return a>b?a=b,1:0;}
void addedge(int u,int v,int w){edge[++elen]={head[u],v,w},head[u]=elen;}
void dijkstra(){
    memset(dis,0x3f,sizeof(dis));
    q.push(node(s,dis[s]=0));
    while(q.size()){
        int u=q.top().v; q.pop();
        if(vis[u])continue; vis[u]=1;
        for(int e=head[u],v;v=edge[e].to,~e;e=edge[e].nex)
            if(chkmin(dis[v],dis[u]+edge[e].dis))q.push(node(v,dis[v]));
    }
}

signed main(){
    memset(head,-1,sizeof(head));
    n=read(),m=read();
    for(int i=1,u,v,w;i<=m;++i)
        u=read(),v=read(),w=read(),
        Edge[u].push_back(Line(v,w,i<<1)),
        Edge[v].push_back(Line(u,w,i<<1|1)),
        addedge(i<<1,i<<1|1,w),addedge(i<<1|1,i<<1,w);
    for(int i=1;i<=n;++i){
        sort(Edge[i].begin(),Edge[i].end());
        for(auto v:Edge[i]){
            if(i==1)addedge(s,v.id,v.w);
            if(v.v==n)addedge(v.id,t,v.w);
        }
        for(int j=1;j<(int)Edge[i].size();++j)
            addedge(Edge[i][j].id,Edge[i][j-1].id,0),
            addedge(Edge[i][j-1].id,Edge[i][j].id,Edge[i][j].w-Edge[i][j-1].w);
    }
    dijkstra();
    printf("%lld\n",dis[t]);
    return 0;
}
posted @ 2021-11-04 19:28  _Famiglistimo  阅读(39)  评论(0编辑  收藏  举报