LCVIII.[POI2006]PRO-Professor Szu
我要举报……本题数据与题面不符(事实上我已经举报了……),会有到不了主楼的情形,要特别考虑。
思路很简单,我们跑SCC缩点。假如一个SCC内部有自环,显然可以一直绕自环,故答案是无限;同时,所有可以走到该SCC的其它点答案都是无限。
于是我们反向所有边,从终点开始拓扑排序,传递无限的情形,并进行DP(设\(f_i\)表示从节点\(i\)到终点的路径数量即可)。注意要先把所有从到不了的SCC连出的边删去,同时不应该考虑终点自身。
代码:
#include<bits/stdc++.h>
using namespace std;
const int lim=36500;
int n,m,dfn[1001000],low[1001000],tot,f[1001000],col[1001000],c,in[1001000],res,cnt;
bool inf[1001000];
vector<int>v[1001000],u[1001000];
stack<int>s;
void Tarjan(int x){
dfn[x]=low[x]=++tot,s.push(x);
for(auto y:v[x]){
if(!dfn[y])Tarjan(y),low[x]=min(low[x],low[y]);
else if(!col[y])low[x]=min(low[x],dfn[y]);
}
if(low[x]<dfn[x])return;
c++;
int y;
do y=s.top(),s.pop(),col[y]=c;while(y!=x);
}
queue<int>q;
int main(){
scanf("%d%d",&n,&m),n++;
for(int i=1,x,y;i<=m;i++)scanf("%d%d",&x,&y),v[y].push_back(x);
for(int i=1;i<=n;i++)if(!dfn[i])Tarjan(i);
for(int i=1;i<=n;i++)for(auto j:v[i])if(col[i]!=col[j])u[col[i]].push_back(col[j]),in[col[j]]++;else inf[col[i]]=true;
for(int i=1;i<=c;i++)if(!in[i])q.push(i);
while(!q.empty()){
int x=q.front();q.pop();
if(x==col[n])continue;
inf[x]=f[x]=0;
for(auto y:u[x])if(!--in[y])q.push(y);
}
f[col[n]]=!inf[col[n]],q.push(col[n]);
while(!q.empty()){
int x=q.front();q.pop();
for(auto y:u[x]){
if(!inf[y])f[y]+=f[x],inf[y]|=inf[x];
if(f[y]>lim)f[y]=0,inf[y]=true;
if(!--in[y])q.push(y);
}
}
for(int i=1;i<=c;i++){
if(inf[i])res=lim+1;
else res=max(res,f[i]);
}
if(res>lim){
puts("zawsze");
for(int i=1;i<n;i++)cnt+=inf[col[i]];
printf("%d\n",cnt);
for(int i=1;i<n;i++)if(inf[col[i]])printf("%d ",i);
}else{
printf("%d\n",res);
for(int i=1;i<n;i++)cnt+=(f[col[i]]==res);
printf("%d\n",cnt);
for(int i=1;i<n;i++)if(f[col[i]]==res)printf("%d ",i);
}
return 0;
}