P3119 [USACO15JAN]草鉴定

约翰有n块草场,编号1到n,这些草场由若干条单行道相连。奶牛贝西是美味牧草的鉴赏家,她想到达尽可能多的草场去品尝牧草。

贝西总是从1号草场出发,最后回到1号草场。她想经过尽可能多的草场,贝西在通一个草场只吃一次草,所以一个草场可以经过多次。

因为草场是单行道连接,这给贝西的品鉴工作带来了很大的不便,贝西想偷偷逆向行走一次,但最多只能有一次逆行。问,贝西最多能吃到多少个草场的牧草。

输入:

第一行:草场数n,道路数m。

以下m行,每行x和y表明有x到y的单向边,不会有重复的道路出现。

输出:

一个数,逆行一次最多可以走几个草场。

输入 #1

7 10 
1 2 
3 1 
2 5 
2 4 
3 7 
3 5 
3 6 
6 5 
7 2 
4 7 

输出 #1
6 
好久没有这么正式的将题面搬过来了;
显然需要tarjan求强连通分量;
但是我只会到这里,但是看题;
允许一条边反向,有点眼熟,啊,那个,就是那个
我们先缩点,dijkstra正向求最长路,再反向求最长路
枚举反向的边;
dis1[x]+dis2[v]-sum[belong[1]];
但是我们要保证能回去
#include<cstdio>
#include<cstring>
#include<queue>
#include<stack>
#include<algorithm>
using namespace std;
const int maxn=1e5+10;
int n,m,pre[maxn],last[maxn],other[maxn],l;
int pre0[maxn],last0[maxn],other0[maxn],l0;
void add0(int x,int y)
{
    l0++;
    pre0[l0]=last0[x];
    last0[x]=l0;
    other0[l0]=y;
}
int dfn[maxn],low[maxn],cnt;
void add(int x,int y)
{
    l++;
    pre[l]=last[x];
    last[x]=l;
    other[l]=y;
}
int pre2[maxn],last2[maxn],other2[maxn],l2;
void add2(int x,int y)
{
    l2++;
    pre2[l2]=last2[x];
    last2[x]=l2;
    other2[l2]=y;
}

stack<int> s;
int belong[maxn],qw;

void dfs(int x)
{
    dfn[x]=low[x]=++cnt;
    s.push(x);
    for(int p=last[x];p;p=pre[p])
    {
        int v=other[p];
        if(!dfn[v])
        {
            dfs(v);
            low[x]=min(low[x],low[v]);
        }
        else if(!belong[v])
        {
            low[x]=min(low[x],dfn[v]);
        }
    }
    if(dfn[x]==low[x])
    {
        belong[x]=++qw;
        //printf("%d ",qw);
        while(1)
        {
            int y=s.top();
            s.pop();
            belong[y]=qw;
            if(x==y) break;
        }
    }
}

int sum[maxn];
int ans;
int vis[maxn];
int dis1[maxn],dis2[maxn];
priority_queue<pair<int,int> >q;
void dijkstra(int x)
{
    memset(vis,0,sizeof(vis));
    dis1[x]=sum[x];
    q.push(make_pair(dis1[x],x));
    while(!q.empty())
    {
        int u=q.top().second;
        q.pop();
    //    printf("%d\n!",u);
    //    printf("%d\n?",last[u]);
    //    if(vis[u]) continue;
    //    vis[u]=1;
        for(int p=last0[u];p;p=pre0[p])
        {
            int v=other0[p];
            //printf("!!");
            if(dis1[v]<dis1[u]+sum[v])
            {
                dis1[v]=dis1[u]+sum[v];
                q.push(make_pair(dis1[v],v));
                //printf("%d\n",dis1[v]);
            }
        }
    }
}

void dijkstra2(int x)
{
    memset(vis,0,sizeof(vis));
    dis2[x]=sum[x];
    q.push(make_pair(dis2[x],x));
    while(!q.empty())
    {
        int u=q.top().second;
        q.pop();
    //    if(vis[u]) continue;
    //    vis[u]=1;
        for(int p=last2[u];p;p=pre2[p])
        {
            int v=other2[p];
            if(dis2[v]<dis2[u]+sum[v])
            {
                dis2[v]=dis2[u]+sum[v];
                q.push(make_pair(dis2[v],v));
            }
        }
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);
    }
    for(int i=1;i<=n;i++)
    {
        if(!dfn[i]) dfs(i);
    }

    //memset(pre,0,sizeof(pre));
    
    for(int i=1;i<=n;i++)
    {
        sum[belong[i]]++;
    }
    //for(int i=1;i<=qw;i++) printf("%d ",sum[i]);
    for(int i=1;i<=n;i++)
    {
        for(int p=last[i];p;p=pre[p])
        {
            int v=other[p];
            if(belong[i]!=belong[v])
            {
                add0(belong[i],belong[v]);
                add2(belong[v],belong[i]);
            }
        }
    }
    ans=sum[belong[1]];    
    dijkstra(belong[1]);
    dijkstra2(belong[1]);
    //for(int i =1;i<=n;i++) printf("%d ",dis1[i]);
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++)    
    {
        if(vis[belong[i]]||!dis1[belong[i]]) continue;
        vis[belong[i]]=1;
        int x=belong[i];
        for(int p=last2[x];p;p=pre2[p])
        {
            int v=other2[p];
            if(!dis2[v]) continue;
            ans=max(ans,dis1[x]+dis2[v]-sum[belong[1]]);
        }
    }
    printf("%d",ans);
    return 0;
}
 
 

 



转载于:https://www.cnblogs.com/WHFF521/p/11545358.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值