#include <iostream>
#include <vector>
using namespace std;

enum color_t {
	WHITE, GRAY, BLACK // Вводим тип данных, отвечающий за цвет вершины
};

vector<color_t> color; // Список, в котором указаны цвета всех вершин
vector<vector<int>> crossroads; // Списки смежности перекрестков

bool is_track(int crossroad, int parent) { // Функция, проверяющая наличие круговой трассы
    if(color[crossroad] == GRAY) return true; // Условие существования цикла
	color[crossroad] = GRAY;
	for(auto next_crossroad : crossroads[crossroad]) { // Переход в соседние вершины
        if(parent != next_crossroad && is_track(next_crossroad, crossroad)) return true; // Вход в вершину, которую еще не входили
	}
	color[crossroad] = BLACK; // Если далее нет цикла, окрашиваем вершину в черный
	return false;
}

int main() {
    int n, m; // Количество перекрестков и дорог
    cin >> n >> m;
    crossroads.resize(n);color.resize(n, WHITE); // Окрашиваем все вершины в белый цвет
    for(int i = 0; i < m; i++) {
        int u, v; // Номер перекрестков, соединенных дорогой
        cin >> u >> v;
        u--;v--; // Так как по условию отсчёт начинается с первой вершины, вычитаем 1 для удобства
        bool already_road = false; // Проверяем, не повторяется ли дорога
        if(!crossroads[u].empty() && !crossroads[v].empty()) {
            int number_of_neighbours = crossroads[u].size();
            for(int j = 0; j < number_of_neighbours && already_road == false; j++) {
                if(crossroads[u][j] == v) already_road = true;
        	}
        }
        if(!(already_road)) { // Проверка на повторение
            crossroads[u].push_back(v); // Добавляем в список доступных перекрестков перекрестка u перекресток v
            crossroads[v].push_back(u); // Аналогичные действия для перекрестка v, чтобы учесть возможность двусторонней езды
        }
    }
    cout << (is_track(0, 0) ? "YES" : "NO"); // Запускаем функцию поиска круговой трассы с первого перекрестка и выводим ответ
}
