洛谷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 '\n'
#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

 

posted @ 2020-07-15 21:10  Anonytt  阅读(189)  评论(0编辑  收藏  举报