//時間計測用 #include #include #include #include #include using namespace std; using namespace atcoder; using ll = long long; //#define endl "\n"; ll N, M, cnt[4]; vector G[300009]; //岩井星人グラフかどうか判定 bool check_iwai(ll x, ll y){ ll line = 0; for(int i = 1; i <= N; i++){ line += G[i].size(); } if(line % 2 != 0) return false; line /= 2; if(!(N == line && N == x * y)) return false; vector cnt(4, 0); for(int i = 1; i <= N; i++) cnt[G[i].size()]++; if(cnt[0] != 0 || cnt[1] != x || cnt[2] != x * (y - 2) || cnt[3] != x) return false; vector dist(N + 1, -1); for(int i = 1; i <= N; i++){ if(dist[i] != -1) continue; if(G[i].size() == 1){ queue que; que.push(i); dist[i] = 0; while(!que.empty()){ ll pos = que.front(); que.pop(); for(auto to: G[pos]){ if(G[to].size() == 3){ if(dist[to] != y) return false; } if(dist[to] == -1){ dist[to] = dist[pos] + 1; que.push(to); } } } } } return true; } //腕の本数x,腕の長さyの岩井星人グラフにできるか bool calc(ll x, ll y){ bool ret = false; vector point; vector chk(N + 1, 0); for(int i = 1; i <= N; i++){ if(chk[i]) continue; if(G[i].size() == 0){ point.push_back(i); chk[i] = 1; continue; } if(G[i].size() == 1){ int pos = i, pre = -1; int num = 1; chk[pos] = 1; while(1){ for(auto to: G[pos]){ if(to == pre) continue; pre = pos; pos = to; break; } chk[pos] = 1; num++; if(num < y){ if(G[pos].size() == 1){ //パスグラフだった point.push_back(pos); break; } if(G[pos].size() == 3){ //腕の長さが足りない point.push_back(i); break; } } if(num == y){ if(G[pos].size() == 2){ point.push_back(pos); } break; } } } } for(int i = 1; i <= N; i++){ if(chk[i] == 0 && G[i].size() == 2) point.push_back(i); } sort(point.begin(), point.end()); point.erase(unique(point.begin(), point.end()), point.end()); if(point.size() == 2){ bool isOK = true; for(auto to: G[point[0]]) if(to == point[1]) isOK = false; for(auto to: G[point[1]]) if(to == point[0]) isOK = false; if(isOK){ G[point[0]].push_back(point[1]); G[point[1]].push_back(point[0]); if(check_iwai(x, y)) ret = true; G[point[0]].pop_back(); G[point[1]].pop_back(); } } return ret; } //次数p1,p2の頂点の1つをp1+1,p2+1にした場合を考える bool check_add_line(ll p1, ll p2){ bool ret = false; cnt[p1]--; cnt[p1 + 1]++; cnt[p2]--; cnt[p2 + 1]++; if(cnt[1] == cnt[3] && cnt[1] >= 3 && gcd(cnt[1], cnt[2]) == cnt[1]){ ll len = cnt[2] / cnt[1] + 2; //腕の長さ ret = calc(cnt[1], len); } cnt[p1]++; cnt[p1 + 1]--; cnt[p2]++; cnt[p2 + 1]--; return ret; } int main(){ cin >> N >> M; if(N - 1 != M){ //XY頂点XY-1辺である条件を満たさない cout << "No" << endl; return 0; } for(int i = 0; i < M; i++){ ll a, b; cin >> a >> b; G[a].push_back(b); G[b].push_back(a); } for(int i = 1; i <= N; i++){ if(G[i].size() >= 4){ //次数が4以上の頂点を含んではいけない cout << "No" << endl; return 0; } cnt[G[i].size()]++; } bool isOK = false; if(cnt[0] == 1){ //0→1,1→2(延長パターン(Y>3)) isOK |= check_add_line(0, 1); //0→1,2→3(腕を増やすパターン(Y=2)) isOK |= check_add_line(0, 2); } if(cnt[0] == 0){ //1→2,1→2(連結パターン(Y>4)) isOK |= check_add_line(1, 1); //1→2,2→3(腕を増やすパターン(Y>2)) isOK |= check_add_line(1, 2); //2→3,2→3(ループを増やすパターン) isOK |= check_add_line(2, 2); } if(isOK) cout << "Yes" << endl; else cout << "No" << endl; return 0; }