BZOJ 1880 [Sdoi2009]Elaxia的路线 - SPFA+拓扑排序

大家都说这是一道大水题。。。想打dyx

应该了解到拓扑排序的功能,类比于食物链那道题,拓扑排序可以dp求出最长链。而在这道题只需求出可以重复的部分搞一个拓扑排序即可。

而怎样求重复的部分呢?有一个思想很好:将一条线路拆分成起点到此的距离和终点到此的距离,跑两遍单源最短路,然后类似地枚举求出一些可以重复的路径,Topo一下就好了

(尝试新代码风格2333,bz会卡空间,实测将边的数量开小一半就可以过了)

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<queue>
#include<algorithm>

using namespace std;

const int maxn=1501;

struct edge
{
    int from,to,next,val;
}oe[maxn*maxn>>1],nowe[maxn*maxn>>1];

int n,ocnt,nowcnt,ans,m,S1,S2,T1,T2;
int ohead[maxn],nowhead[maxn],f[maxn],degree[maxn];
int dists1[maxn],dists2[maxn],distt1[maxn],distt2[maxn];
bool vst[maxn];

void insert(int &cnt,int head[],edge e[],int from,int to,int val)
{
    e[++cnt].from=from;e[cnt].to=to;e[cnt].val=val;e[cnt].next=head[from];head[from]=cnt;
}
void spfa(int S,int head[],edge e[],int dist[])
{
    queue<int>q;
    memset(dist+1,0x3f,sizeof(int)*n);
    memset(vst+1,0,sizeof(bool)*n);
    q.push(S);
    vst[S]=true;dist[S]=0;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vst[u]=false;
        for(int i=head[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if(dist[u]+e[i].val<dist[v])
            {
                dist[v]=dist[u]+e[i].val;
                if(!vst[v])vst[v]=true,q.push(v);
            }
        }
    }
}
void Topological_sort(int head[],edge e[])
{
    memset(f+1,0,sizeof(int)*n);
    queue<int>q;
    for(int i=1;i<=n;i++)
        if(!degree[i])q.push(i);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=head[u];i;i=e[i].next)
        {
            int v=e[i].to;
            f[v]=max(f[v],f[u]+e[i].val);
            ans=max(ans,f[v]);
            degree[v]--;
            if(!degree[v])q.push(v);
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    scanf("%d%d%d%d",&S1,&T1,&S2,&T2);
    for(int i=1,u,v,val;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&val);
        insert(ocnt,ohead,oe,u,v,val);
        insert(ocnt,ohead,oe,v,u,val);
    }
    spfa(S1,ohead,oe,dists1);
    spfa(T1,ohead,oe,distt1);
    spfa(S2,ohead,oe,dists2);
    spfa(T2,ohead,oe,distt2);
    for(int i=1;i<=ocnt;i++)
        if(dists1[oe[i].from]+oe[i].val+distt1[oe[i].to]==dists1[T1])
            if(dists2[oe[i].from]+oe[i].val+distt2[oe[i].to]==dists2[T2])
                insert(nowcnt,nowhead,nowe,oe[i].from,oe[i].to,oe[i].val),degree[oe[i].to]++;
    Topological_sort(nowhead,nowe);
    memset(nowhead+1,0,sizeof(int)*n);
    memset(degree+1,0,sizeof(int)*n);
    nowcnt=0;
    for(int i=1;i<=ocnt;i++)
        if(dists1[oe[i].from]+oe[i].val+distt1[oe[i].to]==dists1[T1])
            if(distt2[oe[i].from]+oe[i].val+dists2[oe[i].to]==distt2[S2])
            insert(nowcnt,nowhead,nowe,oe[i].from,oe[i].to,oe[i].val),degree[oe[i].to]++;
    Topological_sort(nowhead,nowe);
    cout<<ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值