【BZOJ4289】Tax,堆优化dijsktra的最短路问题

Time:2016.08.05
Author:xiaoyimi
转载注明出处谢谢



传送门
思路:
今天模拟题T1
时间全花在这上面了
我以后要因为艹正解而暴力没打完就去吃*

n,m<=10时
直接dfs暴力就好了
n,m<=1000时
考虑化边为点
ans=Σmax(入边,出边),但是我们也不知道对于一个点x来说,哪个是入边哪个是出边
所以把x的出边(在新图中已经化成了点)在新图中互相连边,权值为原图中两边权的较大值
for (int i=first[x];i;i=e[i].next)
for(int j=first[x];j;j=e[j].next)
if (i!=j) 在新图中加边(i,j),权值为max(w[i],w[j])
添加S,T,S向1发出的边(点)连边,n发出的边(点)向T连边
跑最短路即可
显然新图中边是 m2 级别的
n<=100000 m<=200000时
考虑减少边数
对所有x的出边从小到大排序
排序后,每条边i向紧挨着比它大的边i+1连边,权值为w[i+1]-w[i],i+1向i连边,权值为0
类似于补偿流的思想
同时这些出边与其相应的入边(因为是无向图)连边,权值为该出(入)边权值
要解释起来还是有点麻烦啊……
这样的连边是2m左右?反正是m级别的数
然后跑堆优化dijsktra就可以了,SPFA会被卡(但据说SPFA水过80分?)
注意:
重构图这种东西一定不能与原图搞混!
代码:

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#define M 600003
#define LL long long
#define inf 100000000000000000LL
using namespace std;
int n,m,tot,cnt,S,T;
int first[M],First[M];
LL dis[M];
bool vis[M];
struct edge{
    int point,x,y,next;
}e[M<<1];
struct node{
    int P;LL D;
    bool operator <(const node other)const
    {
        return D>other.D;
    }
};
struct E
{
    int X,Y,Z;
    bool operator <(const E other)const
    {
        return Z<other.Z;
    }
}a[M<<1],t[M<<1];
struct Edge{int u,v,w,next;}ee[M<<1];
priority_queue <node> q;
int read()
{
    int t=0;char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') t=(t<<1)+(t<<3)+ch-48,ch=getchar();
    return t;
}
void add(int po,int x,int y)
{
    e[++tot]=(edge){po,x,y,first[po]};
    first[po]=tot;
}
void ADD(int x,int y,int z)
{
    ee[++tot]=(Edge){x,y,z,First[x]};
    First[x]=tot;
}
main()
{
    n=read();m=read();
    int x,y,z;
    for (int i=1;i<=m;i++)
    {
        x=read();y=read();z=read();
        a[++cnt]=(E){x,y,z};
        a[++cnt]=(E){y,x,z};
        add(x,cnt,cnt-1);
        add(y,cnt-1,cnt);
    }
    tot=0;
    S=cnt+1;T=cnt+2;
    for (int i=1;i<=cnt;i++)
    {
        if (a[i].X==1) ADD(S,i,a[i].Z);
        if (a[i].Y==n) ADD(i,T,a[i].Z);
    }
    for (int i=1;i<=n;i++)
    {
        int tmp=0;
        for (int j=first[i];j;j=e[j].next)
            t[++tmp]=(E){e[j].x,e[j].y,a[e[j].x].Z};
        sort(t+1,t+tmp+1);
        for (int i=1;i<=tmp;i++)
            ADD(t[i].X,t[i].Y,t[i].Z);
        for (int i=1;i<tmp;i++)
            ADD(t[i].Y,t[i+1].Y,t[i+1].Z-t[i].Z),
            ADD(t[i+1].Y,t[i].Y,0);
    }
    for (int i=1;i<=T;i++) dis[i]=inf;
    dis[S]=0;
    q.push((node){S,dis[S]});
    for (;!q.empty();q.pop())
    {
        node x=q.top();
        if (!vis[x.P])vis[x.P]=1;
        else continue;
        for (int i=First[x.P];i;i=ee[i].next)
            if (dis[x.P]+ee[i].w<dis[ee[i].v])
                dis[ee[i].v]=dis[x.P]+ee[i].w,
                q.push((node){ee[i].v,dis[ee[i].v]});
    }
    printf("%lld",dis[T]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值