#include <stdio.h>
//思想:并查集
//拥有同一祖先father的顶点一定是联通的,起初将每个顶点的祖先都设为自己,之后每读入一条边x1,x2都将x2的最终祖先的祖先设为x1的祖先
//从逻辑上讲,也就是将以x2祖先为根节点的连接树,接到以x1祖先为根节点的树上,这样两边树上的节点都联通了
//最终通过判断每个节点的最终祖先是否一样,一样则联通
//技巧:在构建祖先树的过程中,可以在找祖先函数Find中,将树高压缩,每次对一个x进行找祖先时候,都将x的直接父亲=x的原始祖先,降低树的高度
//使得最终判断时间能够减少
int father[1001];
int Find(int x) { //找到最原始祖先
  if (x == father[x]) {
    return x;
  } else {
    father[x] = Find(father[x]);
    return father[x];
  }
}

int main() {
  int n, m;
  while (scanf("%d%d", &n, &m) != EOF) {
    for (int i = 1; i <= n; i++) {
      father[i] = i;
    }

    int x1, x2;
    for (int i = 0; i < m; i++) { //记录边的点
      scanf("%d%d", &x1, &x2);
      father[Find(x2)] = Find(x1);

    }
    int tmp = Find(1);
    for (int i = 1; i <= n; i++) {
      if (Find(i) != tmp) {
        printf("NO\n");
        break;
      }
      if (i == n) {
        printf("YES\n");
      }
    }
  }
}