其他分享
首页 > 其他分享> > CF1142E Pink Floyd

CF1142E Pink Floyd

作者:互联网

一、题目

点此看题

二、解法

首先考虑 \(m=0\) 的情况怎么做,发现如果某个点连出去一个菊花的情况,我们都有点搞不定。不难感受到本题直接确定起始点再验证的思路是困难的,我们不妨考虑逐步筛选起始点

套用类似归纳的方法,假设我们现在有集合 \(S\) 表示可能的起始点,其中的点可能管辖一些点,那么走到它就可以走到它管辖的点。我们考虑任取 \(S\) 中的两个点 \((u,v)\) 并询问,那么如果有边 \(u\rightarrow v\),我们把 \(v\) 也纳入 \(u\) 的管辖范围,就可以把 \(v\) 从 \(S\) 中删去,这样就归纳到了子问题,不难分析询问次数 \(O(n)\)(如果边是 \(v\rightarrow u\) 那么处理方式同理)

回到本题,考虑更普遍的情况。由于本题是只能经过同色边,那么在构造中我们尽量要把绿边和粉边独立开来

考虑这样构造初始的 \(S\):选出一个子集,使得它们之间没有粉边连接,并且它们可以到达所有点,换句话说就是这些点通过粉边可以管辖所有点。那么这样场上是不存在粉边的,就可以套用 \(m=0\) 的方法。

但是删除一个点之后,可能 \(S\) 还需要一些改动,那么我们在一开始的时候保留一个拓扑图,发现 \(S\) 中的点都是入度为 \(0\) 的点,在删除一个点之后类似拓扑排序把新释放出来的,入度为 \(0\) 的点加入即可。这样最后剩下的点就是答案,因为它可以通过粉边到达自己初始管辖的点,可以通过绿边到达已经被删除的点。

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

#include <cstdio>
#include <vector>
#include <iostream>
#include <queue>
using namespace std;
const int M = 100005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,vis[M],in[M],d[M];vector<int> g[M],to[M];
void dfs(int u)
{
	vis[u]=in[u]=1;
	for(int v:g[u])
	{
		if(!in[v]) to[u].push_back(v),d[v]++;
		if(!vis[v]) dfs(v);
	}
	in[u]=0;
}
int ask(int u,int v)
{
	printf("? %d %d\n",u,v);
	fflush(stdout);
	return read();
}
signed main()
{
	n=read();m=read();
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read();
		g[u].push_back(v);
	}
	queue<int> q;
	for(int i=1;i<=n;i++)
		if(!vis[i]) dfs(i);
	for(int i=1;i<=n;i++)
		if(!d[i]) q.push(i);
	while(q.size()>1)
	{
		int u=q.front();q.pop();
		int v=q.front();q.pop();
		if(!ask(u,v)) swap(u,v);
		q.push(u);
		for(int x:to[v]) if(!--d[x])
			q.push(x);
	}
	printf("! %d\n",q.front());
}

标签:Pink,粉边,int,CF1142E,read,Floyd,管辖,front,include
来源: https://www.cnblogs.com/C202044zxy/p/16287298.html