P4645 [COCI2006-2007#3] BICIKLI

发布时间 2023-07-09 09:31:46作者: Semorius

P4645 [COCI2006-2007#3] BICIKLI

题意:求一张 \(n\) 个点的有向图中 \(1\) 号点到 \(2\) 号点的路径数。

首先考虑不在 \(1\) 号点到 \(2\) 号点的路径上的那些点不会对答案产生影响,于是先预处理出所有 \(1\) 号点到 \(2\) 号点路径上经过的点。先在原图上以 \(1\) 号点为起点对所有能到达的点进行染色,再在反图上以 \(2\) 号点为起点对所有能到达的点进行染色。两次都被染上色的点就在 \(1\) 号点到 \(2\) 号点的路径上。

然后考虑什么时候会有无数条满足条件的路径。发现当且仅当路径上有环时,可以经过任意次环以产生无数条路径。

剩下的情况就是 \(\text{DAG}\) 了,直接跑拓扑用 \(\text{dp}\) 进行路径计数。

复杂度 \(O(n + m)\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll SIZE = 200005;
const ll mod = 1e9;
ll n, m;
ll head[SIZE], ver[SIZE*2], nxt[SIZE*2], tot;
vector<ll> e[SIZE];
bool c1[SIZE], c[SIZE];
ll d[SIZE];
ll ans[SIZE];

inline ll rd(){
	ll f = 1, x = 0;
	char ch = getchar();
	while(ch < '0' || ch > '9'){
		if(ch == '-') f = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9'){
		x = (x << 1) + (x << 3) + (ch ^ 48);
		ch = getchar();
	}
	return f*x;
}

void add(ll x, ll y){
	ver[++tot] = y, nxt[tot] = head[x];
	head[x] = tot;	
}

void dfs1(ll x){
	c1[x] = 1;
	for(int i = head[x]; i; i = nxt[i]){
		ll y = ver[i];
		if(c1[y]) continue;
		dfs1(y);
	}	
}

void dfs2(ll x){
	c[x] = 1;
	for(int i = 0; i < e[x].size(); i++){
		ll y = e[x][i];
		if(c[y]) continue;
		dfs2(y);
	}
}

struct E{
	ll x, y;
}ee[SIZE];

int main(){
	n = rd(), m = rd();
	assert(n <= 100000 && m <= 100000);
	for(int i = 1; i <= m; i++){
		ll x = rd(), y = rd(); ee[i].x = x, ee[i].y = y;
		add(x, y); e[y].push_back(x);
	}
	dfs1(1);
	dfs2(2);
	ll cnt = 0;
	for(int i = 1; i <= n; i++) c[i] &= c1[i], cnt += c[i];
	for(int i = 1; i <= m; i++){
		if(!c[ee[i].x] || !c[ee[i].y]) continue;
		d[ee[i].y]++; 
	}
	if(d[1] != 0){
		printf("inf");
		return 0;
	} 
	queue<int> q;
	q.push(1); ll z = 0;
	ans[1] = 1;
	while(q.size()){
		ll x = q.front(); q.pop();
		z++;
		for(int i = head[x]; i; i = nxt[i]){
			ll y = ver[i];
			d[y]--; ans[y] = (ans[y] + ans[x]) % mod;
			if(d[y] == 0){
				q.push(y);
			}
		}
	}
	if(z < cnt) printf("inf");
	else printf("%lld", ans[2]);
	return 0;
}