• 洛谷P3436[POI2006]PRO-Professor Szu (Tarjan缩点+反向建边拓扑排序+自环处理+重边处理)


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

    题目大意:给你n个点(第n+1个点为最终点)和m条边,问你从1~n个点走到第n+1个点的不同路线的数量(这里规定路线中但凡有一条不一样,那么这就是不同路线)

    题目陷阱:我们需要考虑对于重边和自环的处理。

    题目解法:我们可以利用Tarjan强联通缩点,缩点完的每个连通块事实上可以看做成可以无限走(因为绕圈嘛),所以我们可以统计每个连通块的数量,如果该连通块数量>1的话就把它的值dp[i]看做LIM,当然我们需要考虑到自环也是可以绕圈走自己,所以当遇到自环情况时,siz[i]+=1,dp[i]=LIM。

    接下来的处理方法就是关于拓扑排序反向建边了,这里不同于以前我写题的习惯(喜欢用记忆化搜索),因为这里会有重边的情况出现,所以我们得用拓扑排序来处理。我们反向建边之后,实际上有一些indeg[i]==0的点他是无效的,因为此点i必定到达不了id[n+1],所以我们可以把id[n+1]的值(即dp[id[n+1]]设置为1,并且对该点打标记,即vis[id[i+1]]=true,以后但凡vis值等于1的都代表可以从终点n+1走到的,是有用点)。接下来就是基本拓扑排序操作了我就不多赘述了,可以看看我的代码。

    #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 '
    '
    #define eps 0.000000001
    #define pb push_back
    #define LIM 36500
    #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=1e6+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 n,m;
    int low[maxn],dfn[maxn],vis[maxn],id[maxn],cnt,tott;
    stack<int> s;int siz[maxn];
    void tarjan(int x){
        low[x]=dfn[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();
                vis[now]=0;
                id[now]=cnt;
                siz[cnt]+=1;
                if(now==x) break;
            }
        }
    }
    int uu[maxn],vv[maxn],indeg[maxn];
    queue<int> q;int dp[maxn];
    void topsort(){
        mem(vis,0);
        dp[id[n+1]]=1;vis[id[n+1]]=1;
        rep(i,1,cnt) if(!indeg[i]){q.push(i);}
        while(!q.empty()){
            int now=q.front();q.pop();
            for(int i=head[now];i!=-1;i=edge[i].next){
                int v=edge[i].to;
                if(--indeg[v]==0){
                    q.push(v);
                }
                if(vis[now]) vis[v]=1;
                dp[v]+=dp[now];
                if(siz[v]>1&&vis[v]) dp[v]=LIM;            
                if(dp[v]>LIM) dp[v]=LIM;
            }
        }
    }
    int f[maxn];
    int main(){
        scanf("%d%d",&n,&m);mem(head,-1);
        tott=cnt=0;
        rep(i,1,m){
            scanf("%d%d",&vv[i],&uu[i]);
            add(uu[i],vv[i]);
        }
        rep(i,1,n+1){
            if(!dfn[i]) tarjan(i);
        }
        mem(head,-1);mem(vis,0);mem(edge,0);tot=0;
        rep(i,1,m){
            if(uu[i]==vv[i]) siz[id[uu[i]]]+=1;
        }
        rep(i,1,m){
            if(id[uu[i]]==id[vv[i]]) continue;
            add(id[uu[i]],id[vv[i]]);
            indeg[id[vv[i]]]+=1;
        }
        topsort();
        int ans=0,num=0;
        rep(i,1,n){
            if(vis[id[i]]){
                f[i]=dp[id[i]];
                ans=max(ans,f[i]);
            }
        }
        rep(i,1,n){
            if(vis[id[i]]){
                if(f[i]==ans) ++num;
            }
        }
        if(ans==LIM) puts("zawsze");
        else cout<<ans<<endl;
        cout<<num<<endl;
        rep(i,1,n){
            if(vis[id[i]]&&f[i]==ans){
                cout<<i<<" ";
            }
        }
    }
    View Code
  • 相关阅读:
    程序员修炼之道:从小工到专家
    2020.12.16收获
    2020.12.15收获
    2020.12.14收获
    2020.12.13收获
    Android学习第二天——对Android的简单了解
    Java学习12.18
    考试加分项
    Java学习12.17
    Java建议
  • 原文地址:https://www.cnblogs.com/Anonytt/p/13308129.html
Copyright © 2020-2023  润新知