LINK

考虑从节点 1 1 1出发,下一个遍历节点一定是 2 2 2,如果不存在 ( 1 , 2 ) (1,2) (1,2)也要补上

Ⅰ.那么到了节点 2 2 2,如果存在 ( 2 , 3 ) (2,3) (2,3)就去遍历节点 3 3 3

Ⅱ.如果不存在 ( 2 , 3 ) (2,3) (2,3),而 2 2 2也不和任何比 3 3 3大的节点相连,那么这个节点就没必要遍历了

因为就算添加一条 ( 2 , 3 ) (2,3) (2,3),也就是让我们到达节点 3 3 3而已

然而我们完全可以选择不添加 ( 2 , 3 ) (2,3) (2,3)而是回溯到节点 1 1 1

如果存在 ( 1 , 3 ) (1,3) (1,3)就剩下了一次操作,不存在则添加 ( 1 , 3 ) (1,3) (1,3),答案不会变差

Ⅲ.如果不存在 ( 2 , 3 ) (2,3) (2,3),且 2 2 2存在比 3 3 3大的节点相连,比如存在 ( 2 , 4 ) (2,4) (2,4)

这个时候 ( 2 , 3 ) (2,3) (2,3)是必须要添加的,否则会直接遍历到节点 4 4 4

所以我们选择添加一条边,遍历节点 3 3 3


综上所诉,贪心就非常简单了.

设置遍历 n x t nxt nxt表示当前期望访问的节点

然后从 1 1 1开始 d f s dfs dfs,按序号小到大访问相邻节点 v v v

v < n x t v<nxt v<nxt,直接跳过

v = = n x t v==nxt v==nxt,访问 n x t nxt nxt n x t + + nxt++ nxt++

v > n x t v>nxt v>nxt,那么添加到 n x t nxt nxt的边并访问 n x t nxt nxt n x t + + nxt++ nxt++

重复次步骤以填充 [ n x t , v ] [nxt,v] [nxt,v]的所有节点

(为了方便让 1 1 1 n + 1 n+1 n+1相连,因为需要遍历所有点)

#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e5+10;
vector<int>vec[maxn];
int nxt,ans,n,m;//下一个期望访问的节点
void dfs(int u)
{
	if( u==n+1 )	return;
	for(auto v:vec[u] )
	{
		if( v<nxt )	continue;
		while( v>=nxt )
		{
			if( v==nxt )	nxt++, dfs( nxt-1 );
			else	nxt++, ans++, dfs( nxt-1 );
		}
	}
} 
int main()
{
	int t; cin >> t;
	while( t-- )
	{
		cin >> n >> m;
		for(int i=1;i<=m;i++)
		{
			int l,r; scanf("%d%d",&l,&r);
			vec[l].push_back( r ); vec[r].push_back( l );
		}
		vec[1].push_back( n+1 );
		for(int i=1;i<=n;i++)	sort( vec[i].begin(),vec[i].end() );
		nxt = 2, ans = 0;
		dfs(1);
		cout << ans << endl;
		for(int i=1;i<=n;i++)	vec[i].clear();
	}
}