BZOJ 3832: [Poi2014]Rally拓扑排序

本文介绍了一种求解拓扑图中最长路径的方法。通过引入超级源点和超级汇点,将问题转化为求这两点间的最长路径。利用拓扑排序和大根堆等数据结构,实现了高效求解算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这道题真的是神思路,不看题解真的是做不出来,我们这样想,我们首先看得出这是一个拓扑图,但是由于拓扑图可能有多个起点和重点,我们搞一个超级源点和超级汇点,这样答案就变成了超级源点到超级汇点的最长路。我们将这个拓扑图分成两个集合,那么这两个集合之间的连边就一定包括当前图的最大路径(因为这条路径必跨越两个集合),那么我们只要维护这两个集合就可以了。我们可以先预处理出来一个f数组和一个g数组,分别代表一个点到超级源和超级汇的最短路,我们就可以给每条边附上一个权值,权值为这条边左端点到超级源的距离和右端点到超级汇的距离加和再加1,这样一条边即可表示经过这条边的最长路,那么两个集合之间只需要一些边来表示不同的路径条数就可以了,那么我们怎么弄最方便呢?:按照拓扑序一个一个把点从右面的集合放到左面的集合是最方便的,那么我们先将所有的g数组加入到一个大根堆中,按照拓扑序每次枚举一个点将其删掉,具体做法是删除g[i],删除所有指向g[i]的边,然后取一个最大值记为删除i这个点的答案,然后加入f[i](因为可能删着删着就会出现路径不连通的现象),再加入所有左端点为i的边(可以手动模拟下,就会明白上面所说的意思),这样找到一个最小的答案输出即可。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<iostream>
#include<iomanip>
#include<ctime>
#include<cmath>
#include<algorithm>
using namespace std;
struct bian
{
    int l,r;
}a[5000000];
int ans_len=0x3f3f3f3f;
int ans;
int fir[5000000];
int _fir[5000000];
int nex[5000000];
int tot=0;
void add_edge(int first[],int l,int r)
{
    a[++tot].l=l;
    a[tot].r=r;
    nex[tot]=first[l];
    first[l]=tot;
}
struct Priority_queue
{
    int dui[1000000];
    int sum[1000000];
    int top;
    Priority_queue()
    {
        top=0;
    }
    void Delete(int v)
    {
        sum[v]--;
    }
    void Push(int v)
    {
        if(sum[v]) sum[v]++;
        else
        {
            sum[v]++;
            dui[++top]=v;
            int i=top;
            while(i>1)
            {
                int j=i>>1;
                if(dui[i]>dui[j]) swap(dui[i],dui[j]),i=j;
                else break;
            }
        }
        return;
    }
    void Pop()
    {
        dui[1]=dui[top];
        top--;
        int i=1;
        while(i*2<=top)
        {
            int j=i*2;
            if(j+1<=top && dui[j+1]>dui[j]) j++;
            if(dui[i]<dui[j]) swap(dui[i],dui[j]),i=j;
            else break;
        }
        return;
    }
    int Top()
    {
        while(!sum[dui[1]]) Pop();
        return dui[1];
    }
}my_queue;
int tuo[1000000];
int rudu[1000000];
int top=1,my_final=1;
int n;
void toposort()
{
    for(int i=1;i<=n;i++)
        if(!rudu[i])
         tuo[my_final++]=i;
    while(top<my_final)
    {
        int u=tuo[top++];
        for(int o=fir[u];o;o=nex[o])
        {
            rudu[a[o].r]--;
            if(!rudu[a[o].r]) tuo[my_final++]=a[o].r;
        }
    }
    top=my_final-1;
}
int f[1000000];
int g[1000000];
int main()
{
    int m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        rudu[r]++;
        add_edge(fir,l,r);
        add_edge(_fir,r,l);
    }
    toposort();
    for(int i=1;i<=top;i++)
    {
        int u=tuo[i];
        f[u]=max(f[u],1);
        for(int o=fir[u];o;o=nex[o]) if(f[a[o].r]=max(f[u]+1,f[a[o].r]));
    }
    for(int i=top;i>=1;i--)
    {
        int u=tuo[i];
        g[u]=max(g[u],1);
        for(int o=fir[u];o;o=nex[o]) g[u]=max(g[a[o].r]+1,g[u]);
    }
    for(int i=1;i<=top;i++) my_queue.Push(g[i]);
    my_queue.Push(0);
    for(int i=1;i<=top;i++)
    {
        int u=tuo[i];
        my_queue.Delete(g[u]);
        for(int o=_fir[u];o;o=nex[o])
            my_queue.Delete(f[a[o].r]+g[a[o].l]);
        if(my_queue.Top()<ans_len) ans_len=my_queue.Top(),ans=u;
        my_queue.Push(f[u]);
        for(int o=fir[u];o;o=nex[o])
            my_queue.Push(f[a[o].l]+g[a[o].r]);
    }
    cout<<ans<<" "<<ans_len-1<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值