解题:POI 2006 PRO-Professor Szu

时间:2023-06-24 21:29:56

题面

这个题是比较套路的做法啦,建反图后缩点+拓扑排序嘛,对于所有处在$size>=2$的SCC中的点都是无限解(可以一直绕)

然后注意统计的时候的小细节,因为无限解/大解也要输出,所以我们把这些点统一统计成36501,然后所有的方案都对36501取min就可以很方便的输出了

 #include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=,INF=;
int P[N],Noww[N],Goal[N];
int p[N],noww[N],goal[N];
int dfn[N],low[N],col[N],deg[N],inf[N];
int stk[N],ins[N],que[N],outp[N],dp[N];
int n,m,c,f,b,t1,t2,cnt,Cnt,tot,top,ans;
void link(int f,int t)
{
noww[++cnt]=p[f];
goal[cnt]=t,p[f]=cnt;
}
void relink(int f,int t)
{
Noww[++Cnt]=P[f];
Goal[Cnt]=t,P[f]=Cnt;
}
void Tarjan_SCC(int nde)
{
dfn[nde]=low[nde]=++tot;
stk[++top]=nde,ins[nde]=true;
for(int i=p[nde];i;i=noww[i])
if(!dfn[goal[i]])
Tarjan_SCC(goal[i]),low[nde]=min(low[nde],low[goal[i]]);
else if(ins[goal[i]])
low[nde]=min(low[nde],low[goal[i]]);
if(dfn[nde]==low[nde])
{
c++; int tmp;
do
{
tmp=stk[top--];
ins[tmp]=false;
col[tmp]=c;
}while(nde!=tmp);
}
}
int main ()
{
scanf("%d%d",&n,&m),b=-;
for(int i=;i<=m;i++)
scanf("%d%d",&t1,&t2),link(t2,t1);
for(int i=;i<=n+;i++)
if(!dfn[i]) Tarjan_SCC(i);
for(int i=;i<=n+;i++)
for(int j=p[i];j;j=noww[j])
if(col[i]!=col[goal[j]])
{
relink(col[i],col[goal[j]]);
deg[col[goal[j]]]++;
}
else inf[col[i]]=true;
for(int i=;i<=c;i++)
if(!deg[i]) que[++b]=i;
dp[col[n+]]=;
while(f<=b)
{
int tn=que[f++];
if(inf[tn]&&dp[tn]) dp[tn]=INF;
for(int i=P[tn];i;i=Noww[i])
{
dp[Goal[i]]=min(dp[Goal[i]]+dp[tn],INF);
if(!(--deg[Goal[i]])) que[++b]=Goal[i];
}
}
for(int i=;i<=c;i++) ans=max(ans,dp[i]);
for(int i=;i<=n;i++)
if(dp[col[i]]==ans) outp[++outp[]]=i;
ans>?printf("zawsze"):printf("%d",ans);
puts(""),printf("%d\n",outp[]);
for(int i=;i<=outp[];i++)
printf("%d ",outp[i]);
return ;
}