洛谷P3119 [USACO15JAN]Grass Cownoisseur G (tarjan缩点+边反向搭建处理)

题目链接:https://www.luogu.com.cn/problem/P3119

题目大意:给你m组单向边关系,你可以选择返回走一次,但是要保证出发点是1和终点是1,问你最大能到达几个点

题目解法:

  首先我们可以通过缩点将一个强连通分量里的所有点变成一个点,并且可以知道缩点以后的图是没有环的,
  此时我们记缩点以后以一号点在第start个强连通分量里;
  然后用spaf得出以start到其他点(缩点以后的点)的最长路dis1[],然后用反向的路在得到所有点到start
  的最长路dis2[],最后我们可以遍历所以路的反向路得到答案;
  ans=max(ans,dis1[b]+dis2[a]-sum[start]);
一些细节:

  SPFA建边的时候,可以少一步w,这个时候把siz[v]可以看做成w。还有就是建边思想,正向建边+反向建边的配合

AC代码:

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,n,a) for(int i=n;i>=a;i--)
#define endl '\n'
#define eps 0.000000001
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a))
#define IO ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
const int INF=0x3f3f3f3f;
const ll inf=0x3f3f3f3f3f3f3f3f;
const int mod=1e9+7;
const int maxn=5e5+5;
int tot,head[maxn];
struct E{
    int to,next;
}edge[maxn<<1];
void add(int u,int v){
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}
int tot1,head1[maxn];
struct E1{
    int to,next,w;
}edge1[maxn<<1];
void add1(int u,int v){
    edge1[tot1].to=v;
    edge1[tot1].next=head1[u];
    head1[u]=tot1++;
}
int tot2,head2[maxn];
struct E2{
    int to,next,w;
}edge2[maxn<<1];
void add2(int u,int v){
    edge2[tot2].to=v;
    edge2[tot2].next=head2[u];
    head2[u]=tot2++;
}
int n,m,uu[maxn],vv[maxn];
int dfn[maxn],low[maxn],id[maxn],vis[maxn],cnt,tott;
stack<int> s;int siz[maxn];
void tarjan(int x){
    dfn[x]=low[x]=++tott;
    s.push(x);vis[x]=1;
    for(int i=head[x];i!=-1;i=edge[i].next){
        int v=edge[i].to;
        if(!dfn[v]){
            tarjan(v);
            low[x]=min(low[x],low[v]);
        }
        else if(vis[v]){
            low[x]=min(low[x],dfn[v]);
        }
    }
    if(low[x]==dfn[x]){
        ++cnt;
        while(1){
            int now=s.top();s.pop();
            id[now]=cnt;
            vis[now]=0;
            siz[cnt]+=1;
            if(x==now) break;
        }
    }
}
int d1[maxn],inq1[maxn];
void SPFA1(int x){
    queue<int> q1;
    d1[x]=siz[x];q1.push(x);inq1[x]=1;
    while(!q1.empty()){
        int now=q1.front();q1.pop();
        inq1[now]=0;
        for(int i=head1[now];i!=-1;i=edge1[i].next){
            int v=edge1[i].to;
            if(d1[v]<d1[now]+siz[v]){
                d1[v]=d1[now]+siz[v];
                if(!inq1[v]){
                    inq1[v]=1;q1.push(v);
                }
            }
        }
    }
}
int d2[maxn],inq2[maxn];
void SPFA2(int x){
    queue<int> q2;
    d2[x]=siz[x];q2.push(x);inq2[x]=1;
    while(!q2.empty()){
        int now=q2.front();q2.pop();
        inq2[now]=0;
        for(int i=head2[now];i!=-1;i=edge2[i].next){
            int v=edge2[i].to;
            if(d2[v]<d2[now]+siz[v]){
                d2[v]=d2[now]+siz[v];
                if(!inq2[v]){
                    inq2[v]=1;q2.push(v);
                }
            }
        }
    }
}
int main(){
    mem(head,-1);mem(head1,-1);mem(head2,-1);
    cin>>n>>m;tott=cnt=0;
    rep(i,1,m){
        cin>>uu[i]>>vv[i];
        add(uu[i],vv[i]);
    }
    rep(i,1,n){
        if(!dfn[i]) tarjan(i);
    }
    rep(i,1,m){
        if(id[uu[i]]==id[vv[i]]) continue;
        add1(id[uu[i]],id[vv[i]]);
        add2(id[vv[i]],id[uu[i]]);
    }
    SPFA1(id[1]);SPFA2(id[1]);
    int ans=siz[id[1]];
    rep(i,1,cnt){
        if(!d1[i]) continue;
        for(int j=head2[i];j!=-1;j=edge2[j].next){
            int v=edge2[j].to;
            if(!d2[v]) continue;
            ans=max(ans,d1[i]+d2[v]-siz[id[1]]);
        }
    }
    cout<<ans<<endl;
}
View Code

 

posted @ 2020-07-17 16:34  Anonytt  阅读(133)  评论(0编辑  收藏  举报