【提高组】强连通分量

最后都变了- 提交于 2019-12-02 00:15:23

P2746 [USACO5.3]校园网Network of Schools

翻译题面:

任务A:求缩点后的图中有多少个点入度为0。

任务B:求入度为0的点数与出度为0的点数的较大值。

注意避免连边时重复算出度入度,所以用set而非vector存图;只有一个点(缩点后)要特判

写代码时注意定义For的话不能写成 For(j,0,g[i].size()-1),查了至少30min...

 

#include <bits/stdc++.h>
#define ri register int
#define For(i,l,r) for(ri i=l;i<=r;i++) 
using namespace std;
const int M=105;
vector<int> g[M];
stack<int> st;
set<int> gg[M];
int n,dfn[M],low[M],inst[M],belong[M],in,cnt,indg[M];
inline void tarjan(int x){
    low[x]=dfn[x]=++in;st.push(x);inst[x]=1;
    //For(i,0,g[x].size()-1){
    for(int i=0;i<g[x].size();i++){
        if(!dfn[g[x][i]]) tarjan(g[x][i]),low[x]=min(low[x],low[g[x][i]]);
        else if(inst[g[x][i]]) low[x]=min(low[x],dfn[g[x][i]]);
    }
    if(dfn[x]==low[x]){
        cnt++;
        while(st.top()!=x){
            belong[st.top()]=cnt;
            inst[st.top()]=0;
            st.pop();
        }
        belong[st.top()]=cnt;
        inst[st.top()]=0;
        st.pop();
    }
}
int main(){
    cin>>n;
    For(i,1,n){int x;while(cin>>x && x)g[i].push_back(x);}
    For(i,1,n){if(!dfn[i])tarjan(i);}
    if(cnt==1) {cout<<1<<endl<<0<<endl;return 0;}
    For(i,1,n){
        //For(j,0,g[i].size()-1){
        for(int j=0;j<g[i].size();j++){
            if(belong[g[i][j]]!=belong[i]&&gg[belong[i]].find(belong[g[i][j]])==gg[belong[i]].end()){
                gg[belong[i]].insert(belong[g[i][j]]);
                indg[belong[g[i][j]]]++;
            }
        }
    }
    int ans1=0,ans2=0;
    For(i,1,cnt){
        if(indg[i]==0) ans1++;
        if(gg[i].size()==0) ans2++;
    }
    cout<<ans1<<endl<<max(ans1,ans2)<<endl;
    return 0;
}
View Code

 

 

P3119 [USACO15JAN]草鉴定Grass Cownoisseur

没有逆行就很简单,那么加上逆行之后我是想建一个反向图,新图和原图各跑一遍SPFA求最长边,然而然后就不知道了。

 

法一.%分%层%图%。

Tarjan缩点+强连通分量不多赘述。

"考虑一张图,将这个图复制一份,点的编号从1~N到(N+1)(N+1)(N+1)~(N+N)(N+N)(N+N)。然后在两层图中连边。对于原图上的每一条边,从原图的指向点到新图的起始点连一条边,边权与原边相同,代表逆向走一条边。逆向走了一条边,就不能再逆向走了,所以从上面的一层(新图)无法回到下面的一层。最后跑一遍SPFA,节点111所在的强连通分量编号,到节点111所在的强连通分量编号+NNN上的最长路,就是最后的答案"(https://www.luogu.org/blog/hsfzLZH1/solution-p3119)

#include<bits/stdc++.h>
#define For(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
const int M=1e5+5;
const int inf=0x3f3f3f;
vector<int> g[M];
vector<int> gg[M<<1];
int n,m,op,in,dfn[M],low[M],bcc[M],siz[M<<1],cnt,dist[M<<1];
bool inst[M<<1];
stack<int>st;
queue<int>q;
inline void tarjan(int x){
    dfn[x]=low[x]=++in;inst[x]=1;st.push(x);
    for(int i=0;i<g[x].size();i++){
        if(!dfn[g[x][i]]) tarjan(g[x][i]),low[x]=min(low[x],low[g[x][i]]);
        else if(inst[g[x][i]]) low[x]=min(low[x],dfn[g[x][i]]);
    }
    if(dfn[x]==low[x]){
        cnt++;
        while(st.top()!=x){
            bcc[st.top()]=cnt;inst[st.top()]=0;st.pop();siz[cnt]++;
        }
        bcc[st.top()]=cnt;inst[st.top()]=0;st.pop();siz[cnt]++;
    }
}
int main(){
    scanf("%d%d",&n,&m);
    while(m--){int u,v; scanf("%d%d",&u,&v);g[u].push_back(v);}
    For(i,1,n) if(!dfn[i])tarjan(i);
    For(i,1,cnt)siz[cnt+i]=siz[i];
    For(i,1,n){
        for(int j=0;j<g[i].size();j++){
            if(bcc[i]!=bcc[g[i][j]]){
                gg[bcc[i]].push_back(bcc[g[i][j]]);
                gg[bcc[g[i][j]]].push_back(bcc[i]+cnt);
                gg[bcc[i]+cnt].push_back(bcc[g[i][j]]+cnt); 
            }
        }
    }
    inst[bcc[1]]=1;q.push(bcc[1]);
    while(!q.empty()){
        int x=q.front();
        for(int i=0;i<gg[x].size();i++){
            if(dist[gg[x][i]]<dist[x]+siz[x]){
                dist[gg[x][i]]=dist[x]+siz[x];
                if(!inst[gg[x][i]]) inst[gg[x][i]]=1,q.push(gg[x][i]);
            }
        }
        q.pop();inst[x]=0;
    }
    printf("%d\n",dist[bcc[1]+cnt]);
    return 0;
}
View Code

 

法二.我“显而易见”(也想了3、5min吧,其实也是比较基础也比较好想的做法)的做法的完整版。

 

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!