bzoj 3887: Grass Cownoisseur Tarjan+Topusort

探讨了在有向图中寻找一条特殊路径的问题,该路径以特定节点为起点和终点,并允许一次反向行走的机会,旨在最大化路径覆盖的节点数量。

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

题目:

给一个有向图,然后选一条路径起点终点都为1的路径出来,有一次机会可以沿某条边逆方向走,问最多有多少个点可以被经过?(一个点在路径中无论出现多少正整数次对答案的贡献均为1)

题解:

首先考虑简单一些的问题
如果没有逆向的机会,那么\(ans\)即为\(1\)所在的强连通分量的大小。
但是现在有一个逆向的机会
如果我们将缩点后的\(DAG\)搞出来的话就可以发现:
一定是从\(1\)的连通块出发走到别的地方然后通过走逆向边返回一个可以到达\(1\)的路径上。
那么我们可以预处理每个点到根的最大\(siz\)之和根到每个点的路上的最大\(siz\)之和。
然后枚举每条边进行\(O(1)\)判断即可.

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
    x=0;char ch;bool flag = false;
    while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
#define rg register int
#define rep(i,a,b) for(rg i=(a);i<=(b);++i)
#define per(i,a,b) for(rg i=(a);i>=(b);--i)
const int maxn = 100010;
struct Edge{
    int to,next;
}G[maxn];
int head[maxn],cnt;
void add(int u,int v){
    G[++cnt].to = v;
    G[cnt].next = head[u];
    head[u] = cnt;
}
int dfn[maxn],low[maxn],dfs_clock;
int sta[maxn],top,belong[maxn];
int scc_cnt,siz[maxn];
#define v G[i].to
void dfs(int u){
    dfn[u] = low[u] = ++ dfs_clock;
    sta[++top] = u;
    for(rg i = head[u];i;i=G[i].next){
        if(!dfn[v]){
            dfs(v);
            low[u] = min(low[u],low[v]);
        }else if(!belong[v]) low[u] = min(low[u],dfn[v]);
    }
    if(dfn[u] == low[u]){
        ++ scc_cnt;
        while(1){
            int x = sta[top--];
            belong[x] = scc_cnt;
            siz[scc_cnt] ++ ;
            if(x == u) break;
        }
    }
}
#undef v
struct Topu{
    struct Edge{
        int to,next;
    }G[maxn];
    int head[maxn],cnt,deg[maxn];
    void add(int u,int v){
        G[++cnt].to = v;
        G[cnt].next = head[u];
        head[u] = cnt;
        ++ deg[v];
    }
#define v G[i].to
    int q[maxn],l,r,f[maxn];
    void bfs(){
        memset(f,-0x3f,sizeof f);
        f[belong[1]] = siz[belong[1]];l = 0;r = -1;
        rep(i,1,scc_cnt){
            if(deg[i] == 0) q[++r] = i;
        }
        while(l <= r){
            int u = q[l++];
            for(rg i = head[u];i;i=G[i].next){
                f[v] = max(f[v],f[u] + siz[v]);
                if(-- deg[v] == 0) q[++r] = v;
            }
        }return ;
    }
#undef v
}a,b;
struct Node{
    int u,v;
}e[maxn];
int main(){
    int n,m;read(n);read(m);
    int u,v;
    rep(i,1,m){
        read(u);read(v);
        e[i].u = u;e[i].v = v;
        add(u,v);
    }
    rep(i,1,n) if(!dfn[i]) dfs(i);
    rep(i,1,m){
        if(belong[e[i].u] == belong[e[i].v]) continue;
        a.add(belong[e[i].u],belong[e[i].v]);
        b.add(belong[e[i].v],belong[e[i].u]);
    }a.bfs();b.bfs();
    int ans = siz[belong[1]] << 1;
    rep(i,1,m){
        if(belong[e[i].u] == belong[e[i].v]) continue;
        ans = max(ans,a.f[belong[e[i].v]] + b.f[belong[e[i].u]]);
    }
    printf("%d\n",ans - siz[belong[1]]);
    return 0;
}

转载于:https://www.cnblogs.com/Skyminer/p/7001379.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值