[POI2006]PRO-Professor Szu

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;
}

posted @ 2021-03-31 14:04  Troverld  阅读(33)  评论(0编辑  收藏  举报