luogu P3119 [USACO15JAN]草鉴定Grass Cownoisseur

这道题显然要先 Tarjan 缩点预处理;
这里就不多说了;
之后的点都是缩点之后点集;

我们考虑两种点:

<1> 以 1 为起点可以直接到达的; 我们这里叫它一类点;
<2> 以该点为起点,可以直接到达 1 的; 我们这里叫它二类点;

所以我们先用 spfa 处理出来从 1 到这两种点的点权值;
统计 ans 用;
然后枚举每一个一类点;
由于我们只能走一次反边;
所以我们所要的反边一定与一类点和二类点相连;
因为这样才能确保从 1 出发,再回到 1 ;
所以我们可以确定一类点和二类点相连的边一定满足;
那么我们枚举这些边;
然后统计所连两点的点权和;
更新 ans ;
但是由于在一开始 spfa 处理时;
1 这个点的点权计算了两遍;
所以最后要减掉;


#include "iostream"
#include "stdio.h"
#include "algorithm"
#include "vector"
#include "map"
#include "queue"
#define II int
#define R register
#define I 123546
using namespace std;


struct node {
    II to,up;
} aa[I], aa_1[I];

II head[I], DFN[I], LOW[I], is[I], stack[I], vis[I], belong[I], nu[I];

II inq[I], in_just[I], dis_back[I], in_back[I], dis_just[I], head_1[I], dis[I];

II _tot,n,m,_top,_num,bit,ans,_tot_1;

vector <II> just[I], back[I];

queue <II> Q;

void add(R II x,R II y)
{
    aa[++_tot].to=y;
    aa[_tot].up=head[x];
    head[x]=_tot;
}

void  add_1(R II x,R II y)
{
    aa_1[++_tot_1].to=y;
    aa_1[_tot_1].up=head_1[x];
    head_1[x]=_tot_1;
}


void Tarjan(R II x)
{
    DFN[x]=LOW[x]=++_num;
    stack[++_top]=x; vis[x]=1;
    for(R II i=head[x];i;i=aa[i].up) 
    {
        R II go=aa[i].to;
        if(!DFN[go]) {
            Tarjan(go);
            LOW[x]=min(LOW[x],LOW[go]);
        } else if(vis[go]) LOW[x]=min(LOW[x],DFN[go]);
    }

    if(LOW[x]==DFN[x]) {
        bit++;
        do {
            R II o=stack[_top--];
            vis[o]=0;
            belong[o]=bit;
            nu[bit]++;
        } while (stack[_top+1]!=x) ;
    }
}


void dfs_just(R II x)
{
    in_just[x]=1;
    for(R II i=0;i<just[x].size();i++)
    {
        R II go=just[x][i];
        if(!in_just[go]) {
            dfs_just(go);
        }
    }
}


void dfs_back(R II x)
{
    in_back[x]=1;
    for(R II i=0;i<back[x].size();i++)
    {
        R II go=back[x][i];
        if(!in_back[go]) {
            dfs_back(go);
        }
    }
}


void spfa_just()
{
    for(R II i=1;i<=bit;i++) dis_just[i]=-1e9;
    Q.push(belong[1]); inq[belong[1]]=1;
    dis_just[belong[1]]=nu[belong[1]];

    while (!Q.empty()) {
        R II x=Q.front(); Q.pop(); inq[x]=0;
        for(R II i=0;i<just[x].size();i++)
        {
            R II go=just[x][i];
            if(dis_just[go]<dis_just[x]+nu[go]) {
                dis_just[go]=dis_just[x]+nu[go];
                if(!inq[go]) {
                    Q.push(go);
                    inq[go]=1;
                }
            }
        }
    }
}


void spfa_back()
{
    for(R II i=1;i<=bit;i++) dis_back[i]=-1e9;
    Q.push(belong[1]); inq[belong[1]]=1;
    dis_back[belong[1]]=nu[belong[1]];

    while (!Q.empty()) {
        R II x=Q.front(); Q.pop(); inq[x]=0;
        for(R II i=0;i<back[x].size();i++)
        {
            R II go=back[x][i];
            if(dis_back[go]<dis_back[x]+nu[go]) {
                dis_back[go]=dis_back[x]+nu[go];
                if(!inq[go]) {
                    Q.push(go);
                    inq[go]=1;
                }
            }
        }
    }
}


int main()
{
//  freopen("1.in","r",stdin);

    scanf("%d%d",&n,&m);
    for(R II i=1;i<=m;i++)
    {
        R II x,y;
        scanf("%d%d",&x,&y);
        add(x,y);
    }

    for(R II i=1;i<=n;i++)
    {
        if(!DFN[i]) {
            Tarjan(i);
        }
    }
    // 缩点; 

    for(R II i=1;i<=n;i++)
    {
        for(R II j=head[i];j;j=aa[j].up)
        {
            R II go=aa[j].to;
            if(belong[go]!=belong[i]) {
                just[belong[i]].push_back(belong[go]);
                back[belong[go]].push_back(belong[i]);
            }
        }
    }
    // 建正反边; 

    dfs_just(belong[1]);
    dfs_back(belong[1]);
    // 记录一、二类点; 

    spfa_just();
    spfa_back();
    // 计算权值; 

    for(R II i=1;i<=bit;i++)
    {
        if(in_just[i]) {
            for(R II j=0;j<back[i].size();j++)
            {
                R II go=back[i][j];
                if(in_back[go]) {
                    ans=max(ans,dis_just[i]+dis_back[go]);
                }
            }
        }
    }
    // 枚举合法的边; 

    printf("%d\n",ans-nu[belong[1]]);
    // 减去 1 多算的的权值; 
    exit(0);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值