C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int h[N], e[N * 2], ne[N * 2], idx;
int in[N]; //这个in[u]表示的是第u个点的入度是多少。
bool st[N]; //这个数组的作用就是在下面表示每个点有没有被删除或者说是当前这个点有没有被考虑到的。
int n, m;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
void init() {
memset(h, -1, sizeof h);
memset(e, 0, sizeof e);
memset(ne, 0, sizeof ne);
memset(in, 0, sizeof in);
memset(st, 0, sizeof st);
idx = 0;
}
void solve() {
cin >> n >> m;
init();
for (int i = 0; i < m; i ++ ) {
int a, b;
cin >> a >> b;
add(a, b), add(b, a);
in[a] ++, in[b] ++;
}
// 根据度为1的点(叶子节点)删去所有链和单点, 只留下环
//经过我的验证,下面就是再把所有链给删除了的,删除的原理就是从入度为1的店开始,和他链接的不是章鱼环上的店可能有等于2的,大于2的,遇到等于2的,则这个点也必定是链上的店,压入进队列。
//大于2的可能是链上的,也可能是章鱼环上的,是章鱼环上的则必定是最后一次出现的入度大于2的点的,显然这个点后面没有再往队列中压入元素的。如果入度大于2的点不是环上的点则必定每次都会被一个入度为1的点给其入度依次-1直到入度也为1的。
//经过我的验证下面就是把所有单点和链都给删除掉了的。
for (int i = 1; i <= n; i ++ )
{
if (in[i] == 1) //这个就是在找出所有入度为1的点。
{
queue<int> q;
q.push(i);
st[i] = true; //这个st[i] = true就是表示第i个点就是要被删掉的点。
while (q.size())
{
int t = q.front();
q.pop();
for (int j = h[t]; j != -1; j = ne[j]) //这个就是与这个入度为1的点链接的点。
{
int k = e[j];
if (!st[k])
{
if (in[k] == 2)
{
q.push(k);
st[k] = true;
}
else if (in[k] > 2) //入度大于2就是说明这个点是链接了链的,链上的点删除了,这个点的入度肯定就是要进行-1了的。
{
in[k] --;
}
}
}
}
}
else if (in[i] == 0) //这个就是说明是单点的意思。
{
st[i] = true;
}
}
// 判断剩下的环中是否有度全为2的章鱼环
int cnt = 0, res = 0;
for (int i = 1; i <= n; i ++ )
if (!st[i])
{
st[i] = true; //在这里面就不是和上面一个循环的解释一样的了,这个就是表示当前点以及被考虑过了的。
queue<int> q;
q.push(i);
bool flag = true; // 是否是章鱼环
int len = 0; // 长度
while (q.size())
{
int t = q.front();
q.pop();
if (in[t] > 2) flag = false; //就是像例子2中的两个环连在一起就是一个环都不算的。但是不能break的,因为就像上面的样例3,可能就是分开为两个完全独立的环的。
len ++; // 因为删除了所有链和单点只有环了
for (int j = h[t]; j != -1; j = ne[j])
{
int k = e[j];
if (!st[k])
{
st[k] = true;
q.push(k);
}
}
}
if (flag) res = len; // 如果是章鱼环的话更改答案
cnt += flag; // 章鱼环的个数
}
if (cnt == 1) cout << "Yes" << ' ' << res << endl;
else cout << "No" << ' ' << cnt << endl;
}
int main() {
int T;
cin >> T;
while (T --)
solve();
return 0;
}