洛谷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<<" "; } } }
前ICPC算法竞赛退役选手|现摸鱼ing