BZOJ 4289: PA2012 Tax

18 篇文章 0 订阅
4 篇文章 0 订阅

Description

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

Solution

神题搞得我欲仙欲死,竟不知从何说起
看到这题第一反应是直接跑最短路,但并过不了
所以考虑重新构图
显然按边构图(把边作为新图中的点)大法好
首先先把无向边都搞成有向边
采用网络流的思想,我们对于以 x 为起点的有向边,我们把这些边按照边权从小到大排序,然后依次(排序后相邻的)连边,比他大的连边权为原边权差的边,小的连边权为0的边。
然后所有正向边和反向边之间互相连边,边权为原边边权的大小
设立一个虚拟源点和虚拟汇点,源点向所有以1为起点的边连边,所有以 n 为终点的边向汇点连边,边权都为原边权。
这样的正确性是显然的(画个图可以知道)
而边的个数级别是O(m)
最后跑 src sink 的最短路即可

Code

/**************************************************************
    Problem: 4289
    User: bblss123
    Language: C++
    Result: Accepted
    Time:3468 ms
    Memory:43160 kb
****************************************************************/

#include<iostream>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<time.h>
#include<stdlib.h>
#include<math.h>
#include<string>
#include<vector>
#include<queue>
#include<stack>
#include<set>
#include<map>
using namespace std;
typedef double db;
typedef unsigned ud;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef vector<int> vec;
typedef vector<pii> vecp;

#define fi first 
#define se second
#define pb push_back
#define ph push

const int INF=(ud)-1>>1;
const ll inf=(ull)-1ll>>1;

template<class T>void rd(T &a){
    a=0;char c;
    while(c=getchar(),!isdigit(c));
    do a=a*10+(c^48);
        while(c=getchar(),isdigit(c));
}
template<class T>void nt(T x){
    if(!x)return;
    nt(x/10);
    putchar(48+x%10);
}
template<class T>void pt(T x){
    if(!x)putchar('0');
    else nt(x);
}
template<class T>void Max(T &a,T b){
    if(a<b)a=b;
}
template<class T>void Min(T &a,T b){
    if(a==-1||a>b)a=b;
}
const int M=1e5+5;
const int N=2e5+5;
struct Slide{
    int a,b,c;
}slid[N<<1];
struct node{
    int to;ll v;
    inline bool operator < (const node &tmp)const{
        return v>tmp.v;
    }
};
int que[M];
struct Edge{
    int to,c,nxt;
}G[N<<2];
int head[N],tot_edge;
inline void add_edge(int from,int to,int c){
    G[tot_edge]=(Edge){to,c,head[from]};
    head[from]=tot_edge++;
    G[tot_edge]=(Edge){from,c,head[to]};
    head[to]=tot_edge++;
}
inline bool cmp(const int &a,const int &b){
    return slid[a].c<slid[b].c;
}
vector<node>edge[N<<1];
int src,sink,n,m,cnt;
priority_queue<node>pque;
ll d[N<<1];
bool mark[N<<1];
inline ll Djstl(){
    for(int i=0;i<=sink;++i)
        d[i]=inf;
    for(pque.ph((node){src,d[src]=0});!pque.empty();){
        node now=pque.top();pque.pop();
        int v=now.to;
        if(v==sink)return now.v;
        if(mark[v])continue;
        mark[v]=1;
        for(int i=0;i<edge[v].size();++i){
            node nxt=edge[v][i];
            if(d[nxt.to]>d[v]+nxt.v){
                d[nxt.to]=d[v]+nxt.v;
                pque.ph((node){nxt.to,d[nxt.to]});
            }
        }
    }
}
int main(){
    cin>>n>>m;
    memset(head,-1,sizeof(head));
    tot_edge=0;
    for(int i=1,a,b,c;i<=m;++i){
        rd(a),rd(b),rd(c);
        slid[cnt++]=(Slide){a,b,c};
        slid[cnt++]=(Slide){b,a,c};
        add_edge(a,b,c);
    }
    for(int i=1;i<=n;++i){
        int s=0;
        for(int j=head[i];~j;j=G[j].nxt)
            que[s++]=j;//这些点是以i为起点的边的编号 
        for(int j=0;j<s;++j)
            edge[que[j]].pb((node){que[j]^1,slid[que[j]].c});
        sort(que,que+s,cmp);
        for(int j=0;j<s-1;++j){
            edge[que[j]].pb((node){que[j+1],slid[que[j+1]].c-slid[que[j]].c});
            edge[que[j+1]].pb((node){que[j],0});
        }
    }
    src=cnt,sink=src+1;
    for(int i=0;i<cnt;++i){
        if(slid[i].a==1)edge[src].pb((node){i,slid[i].c});
        if(slid[i].b==n)edge[i].pb((node){sink,slid[i].c});
    }
    cout<<Djstl()<<endl;
    return 0;
}

太弱啦这种题写了好多天(虽说中间放假了)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值