洛谷P3119

省选难度啊啊啊
经评论区的朋友提醒,代码已订正
先说一下该题思路:
首先,这题并非是求最短路,而是求最长路(最长路常用算法一般是拓扑排序,而我这个蒟蒻还没有学会QAQ)
但是这一题既然标签是连通图,那么肯定要用tarjan,考虑到缩点之后每个缩点都具有一定数量的点数,然后如果我们进入了这个缩点,那么我们可以从进入该缩点的点起,然后将整个缩点中的点全部遍历一遍,所以我们可以将缩点中的点数作为边的权值(既然有权值那么我们就可以跑spfa),进行缩点之后就可以重新建图,所以在这里我们有三个first,nxt,v,然后我们要跑两边spfa,所以我们要有两个dis数组来保存正向和逆向的距离,然后只需要枚举每个点,同时用最大值来更新ans

#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
#include<queue>
using namespace std;
const int maxn=100003;
stack<int> s;
queue<int> q;
int tot,tot1,tot2;
int v[maxn],v1[maxn],v2[maxn];
int nxt[maxn],nxt1[maxn],nxt2[maxn];
int first[maxn],f1[maxn],f2[maxn];
int ins[maxn],dfn[maxn],low[maxn];
int c[maxn],cnt[maxn],use[maxn];
int dis1[maxn],dis2[maxn];
int timing,col;
void add(int x,int y)
{
    v[tot]=y;
    nxt[tot]=first[x];
    first[x]=tot++;
}
void add1(int x,int y)
{
    v1[tot1]=y;
    nxt1[tot1]=f1[x];
    f1[x]=tot1++;
}
void add2(int x,int y)
{
    v2[tot2]=y;
    nxt2[tot2]=f2[x];
    f2[x]=tot2++;
}
void tarjan(int x)
{
    ins[x]=1;
    s.push(x);
    dfn[x]=low[x]=++timing;
    for(int to=first[x];to!=-1;to=nxt[to])
    {
        if(!dfn[v[to]])
        {
            tarjan(v[to]);
            low[x]=min(low[x],low[v[to]]);
        }
        else if(ins[v[to]])
            low[x]=min(low[x],low[v[to]]);
    }
    if(dfn[x]==low[x])
    {
        col++;
        int k;
        do
        {
            k=s.top();
            ins[k]=0;
            c[k]=col;
            cnt[col]++;
            s.pop();
        } while (k!=x);
    }
}
void spfa1(int x)
{
    dis1[x]=cnt[x];
    q.push(x);
    while(!q.empty())
    {
        int k=q.front();
        q.pop();
        for(int to=f1[k];to!=-1;to=nxt1[to])
        {
            if(dis1[v1[to]]<dis1[k]+cnt[v1[to]])
                dis1[v1[to]]=dis1[k]+cnt[v1[to]];
            if(!ins[v1[to]])
            {
                q.push(v1[to]);
                ins[v1[to]]=1;
            }
        }
        ins[k]=0;
    }
}
void spfa2(int x)
{
    dis2[x]=cnt[x];
    q.push(x);
    while(!q.empty())
    {
        int k=q.front();
        q.pop();
        for(int to=f2[k];to!=-1;to=nxt2[to])
        {
            if(dis2[v2[to]]<dis2[k]+cnt[v2[to]])
                dis2[v2[to]]=dis2[k]+cnt[v2[to]];
            if(!ins[v2[to]])
            {
                q.push(v2[to]);
                ins[v2[to]]=1;
            }
        }
        ins[k]=0;
    }
}
int main()
{
    int n,m,a,b,start;
    cin>>n>>m;
    memset(first,-1,sizeof(first));
    memset(f1,-1,sizeof(f1));
    memset(f2,-1,sizeof(f2));
    for(int k=0;k<m;k++)
    {
        scanf("%d%d",&a,&b);
        add(a,b);
    }
    for(int i=1;i<=n;i++)
        if(!dfn[i])
            tarjan(i);
    start=c[1];
    for(int i=1;i<=n;i++)
        for(int to=first[i];to!=-1;to=nxt[to])
            if(c[i]!=c[v[to]])
            {
                add1(c[i],c[v[to]]);
                add2(c[v[to]],c[i]);
            }
    spfa1(start);
    spfa2(start);
    int ans=cnt[start];
    for(int i=1;i<=n;i++)
        if(!use[c[i]] && dis1[c[i]])
        {
            use[c[i]]=1;
            for(int to=f2[c[i]];to!=-1;to=nxt2[to])
            {
                if(!dis2[v2[to]])
                    continue;
                ans=max(dis1[c[i]]+dis2[v2[to]]-cnt[start],ans);
            }
        }
    cout<<ans;
    return 0;
}

好像说了这么久,应该只有我自己一个人懂吧233333

另一个神犇的分层法暂时还没懂23333

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值