1. 程式人生 > 實用技巧 >CF1361E James and the Chase

CF1361E James and the Chase

James and the Chase

James Bond has a new plan for catching his enemy. There are some cities and directed roads between them, such that it is possible to travel between any two cities using these roads. When the enemy appears in some city, Bond knows her next destination but has no idea which path she will choose to move there.

The city \(a\) is called interesting, if for each city \(b\), there is exactly one simple path from \(a\) to \(b\). By a simple path, we understand a sequence of distinct cities, such that for every two neighboring cities, there exists a directed road from the first to the second city.

Bond's enemy is the mistress of escapes, so only the chase started in an interesting city gives the possibility of catching her. James wants to arrange his people in such cities. However, if there are not enough interesting cities, the whole action doesn't make sense since Bond's people may wait too long for the enemy.

You are responsible for finding all the interesting cities or saying that there is not enough of them. By not enough, James means strictly less than \(20\%\) of all cities.

\(1\leq n\leq 10^5,0\leq m\leq 2\times 10^5\).

Tutorial (en)

First, let us describe an algorithm for checking if a vertex is interesting. Let \(v\)

be the vertex we want to check. Find any DFS tree rooted in that vertex. We can see that \(v\) is interesting if and only if that tree is unique. The tree is unique iff every non-tree edge leads from some vertex \(u\) to \(u\)'s ancestor. That condition can be easily checked in \(\mathcal{O}(n)\).

只有返祖邊才合法。

Using the fact that we are interested only in cases when at least \(20\%\) of vertices are interesting, we can find any interesting vertex by choosing a random vertex, checking if it is interesting and repeating that algorithm \(T\) times or until we find an interesting vertex. For \(T = 100\) the probability of failure is around \(2 \cdot 10^{-10}\).

Denote that vertex as \(r\). Find the DFS tree rooted in that vertex. We can notice that vertex is interesting if and only if it has a unique path to all of its ancestors in this tree. We will say that edge is passing vertex \(v\) if it starts in its subtree (including \(v\)) and ends in one of the proper ancestors of \(v\). It is evident that if two edges pass through \(v\), then \(v\) cannot be interesting.

Let us pick vertex \(v\) such that there is at most one edge passing through it. It is evident that if \(v \neq r\), then there is at least one such edge (because our graph is strongly connected). Let \(u\) be the endpoint of this edge being a proper ancestor of \(v\). We prove that \(v\) is interesting if and only if \(u\) is interesting.

無趣點是具有傳遞性的。

Pick arbitrary ancestor of \(v\) which is not an ancestor of \(u\), let us denote it by \(t\). There is precisely one simple path from \(v\) to \(t\) – descending in the subtree of \(v\), using the non-tree edge to \(u\) and then tree edges to reach \(t\).

Now pick arbitrary common ancestor of \(v\) and \(u\), let it be \(k\). If there is more than one simple path from \(u\) to \(k\), then obviously there is more than one simple path from \(v\) to \(k\). Otherwise, there is precisely one simple path from \(v\) to \(k\) – descend in the subtree, use edge to \(u\) and from \(u\) there is one simple path to \(k\).

The above observations allow computing interesting vertices using simple DFSs. The final complexity is \(\mathcal{O}(Tn)\).

CO int N=1e5+10;
int n;
vector<int> to[N];

int vis[N];
bool interesting;

void dfs(int x){
	vis[x]=1;
	for(int y:to[x]){
		if(!vis[y]) dfs(y);
		else if(vis[y]==2) interesting=0;
	}
	vis[x]=2;
}
bool check(int r){
	fill(vis+1,vis+n+1,0),interesting=1;
	dfs(r);
	return interesting;
}

bool bad[N];
int lvl[N],best[N],balance[N];

int find_bad(int x){
	vis[x]=1;
	best[x]=x;
	for(int y:to[x]){
		if(!vis[y]){
			lvl[y]=lvl[x]+1;
			balance[x]+=find_bad(y); // how many edges to ancestor
			if(lvl[best[y]]<lvl[best[x]]) best[x]=best[y];
		}
		else{
			++balance[x],--balance[y];
			if(lvl[y]<lvl[best[x]]) best[x]=y;
		}
	}
	if(balance[x]>1) bad[x]=1;
	return balance[x];
}
void propagate_bad(int x){
	vis[x]=1;
	if(!bad[x] and bad[best[x]]) bad[x]=1;
	for(int y:to[x])if(!vis[y]) propagate_bad(y);
}

void real_main(){
	read(n);
	for(int i=1;i<=n;++i) to[i].clear();
	for(int m=read<int>();m--;){
		int x=read<int>(),y=read<int>();
		to[x].push_back(y);
	}
	int id=-1;
	for(int t=100;t--;){
		int r=gen()%n+1;
		if(check(r)) {id=r; break;}
	}
	if(id==-1) {puts("-1"); return;}
	for(int i=1;i<=n;++i){
		vis[i]=bad[i]=0;
		lvl[i]=best[i]=balance[i]=0;
	}
	find_bad(id);
	fill(vis+1,vis+n+1,0);
	propagate_bad(id);
	vector<int> ans;
	for(int i=1;i<=n;++i)if(!bad[i]) ans.push_back(i);
	if(5*(int)ans.size()>=n){
		for(int x:ans) printf("%d ",x);
		puts("");
	}
	else puts("-1");
}
int main(){
	srand(20030506);
	for(int T=read<int>();T--;) real_main();
	return 0;
}