//TLE #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 chkcnt(4, 0); for(int i = 1; i <= N; i++) cnt[G[i].size()]++; if(chkcnt[0] != 0 || chkcnt[1] != x || chkcnt[2] != x * (y - 2) || chkcnt[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; } if(chk[pos] == 1){ //探索済みならこの先に候補はない break; } if(G[pos].size() < 3) 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); } for(int i = 0; i < (int)point.size(); i++){ for(int j = i + 1; j < (int)point.size(); j++){ bool isOK = true; for(auto to: G[point[i]]) if(to == point[j]) isOK = false; for(auto to: G[point[j]]) if(to == point[i]) isOK = false; if(isOK){ G[point[i]].push_back(point[j]); G[point[j]].push_back(point[i]); if(check_iwai(x, y)) ret = true; G[point[i]].pop_back(); G[point[j]].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]++; //cout << cnt[0] << " " << cnt[1] << " " << cnt[2] << " " << cnt[3] << endl; 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; }