• P3119 [USACO15JAN]草鉴定Grass Cownoisseur


    传送门

    显然每次走到一个联通块肯定要把整个联通块的草场都走一遍,考虑缩点

    然后直接建分层图跑最长路就好了

    (为了方便,以下的强连通分量均称为点)

    但是有一个小问题,如果反着走可能走到以前走过的点,怎么判断(因为每个点只有一次贡献)

    其实根本不用判断,因为如果从一号点出发,走出去后要走回来一定要逆行一次(因为没有环)

    如果你把逆行机会用在走以前走过的点(出发点除外),那你就永远走不回出发点了

    所以不会对答案产生贡献,然后就是Dijk跑最长路了

    分层图十分显然,dis[ i ] [ 0/1 ] 表示到达点 i ,是否用过逆行机会时的最长路

    建图时记得建一下反图就好了

    // luogu-judger-enable-o2
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<iostream>
    #include<vector>
    #include<queue>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=2e5+7;
    int n,m;
    int fir[N],from[N<<1],to[N<<1],cntt=0;
    inline void add(int &a,int &b)
    {
        from[++cntt]=fir[a];
        fir[a]=cntt; to[cntt]=b;
    }
    //以下tarjan模板
    int dfn[N],low[N],dfs_clock,st[N],Top,be[N],tot,cnt[N];
    void Tarjan(int x)
    {
        dfn[x]=low[x]=++dfs_clock; st[++Top]=x;
        for(int i=fir[x];i;i=from[i])
        {
            int &v=to[i];
            if(!dfn[v]) Tarjan(v),low[x]=min(low[x],low[v]);
            else if(!be[v]) low[x]=min(low[x],dfn[v]);
        }
        if(low[x]==dfn[x])
        {
            tot++; cnt[tot]=1;
            while(st[Top]!=x)
            {
                cnt[tot]++;
                be[st[Top--]]=tot;
            }
            be[st[Top--]]=tot;
        }
    }
    vector <int> v1[N],v2[N];//为了方便直接用vector存图,v1是正图,v2是反图
    void build()//建图
    {
        for(int i=1;i<=n;i++)
            for(int j=fir[i];j;j=from[j])
            {
                int &v=to[j]; if(be[i]==be[v]) continue;
                v1[be[i]].push_back(be[v]); v2[be[v]].push_back(be[i]);
            }
    }
    int dis[N][2];
    struct data
    {
        int pos,dis,p;//当前位置,经过距离,是否逆行过
        inline bool operator < (const data &tmp) const {
            return dis<tmp.dis;
        }
    };
    priority_queue <data> q;
    void Dijk()
    {
        memset(dis,128,sizeof(dis)); q.push((data){be[1],0,0}); dis[be[1]][0]=0;//初始状态
        data x;
        while(!q.empty())
        {
            x=q.top(); q.pop(); if(dis[x.pos][x.p]!=x.dis) continue;
            int len=v1[x.pos].size();
            for(int i=0;i<len;i++)
            {
                int &v=v1[x.pos][i];
                if(dis[v][x.p]<x.dis+cnt[v])
                    dis[v][x.p]=x.dis+cnt[v],q.push((data){v,dis[v][x.p],x.p});
            }
            if(!x.p)//如果没逆行过就考虑逆行
            {
                len=v2[x.pos].size();
                for(int i=0;i<len;i++)
                {
                    int &v=v2[x.pos][i];
                    if(dis[v][1]<x.dis+cnt[v])
                        dis[v][1]=x.dis+cnt[v],q.push((data){v,dis[v][1],1});
                }
            }
        }
    }
    int main()
    {
        int a,b;
        n=read(); m=read();
        for(int i=1;i<=m;i++)
        {
            a=read(); b=read();
            add(a,b);
        }
        for(int i=1;i<=n;i++)
             if(!dfn[i])    Tarjan(i);//记得每个点都要缩起来,正图走不通可能反图能走
        if(tot==1) { printf("%d",n); return 0; }//记得特判一波
        build();
        Dijk();
        printf("%d",max(dis[be[1]][0],dis[be[1]][1]));
        return 0;
    }
  • 相关阅读:
    构造回文的最小插入次数
    动态规划设计:最大子数组
    状态压缩:对动态规划进行降维打击
    团灭 LeetCode 股票买卖问题
    经典动态规划:戳气球
    贪心算法之区间调度问题
    微信分享 添加URL Schemes
    UITouch的用法
    HTTP协议详解
    经典SQL语句大全
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/9900015.html
Copyright © 2020-2023  润新知