#include <bits/stdc++.h>
using namespace std;
const int N = 1000000000;
int tree[5001000], a[3001000], n, m;
vector<int> v;
struct query {
  int x, y, z, opt;
} q[3001000];
struct Segment_Tree {
  int val, cnt, no1, no2;
  Segment_Tree operator+(Segment_Tree b) const {
    Segment_Tree res;
    if (val > b.val) return *this;
    if (val < b.val) return b;
    if (cnt == 2) return *this;
    if (b.cnt == 2) return b;
    res.val = val, res.cnt = 2;
    res.no1 = no1, res.no2 = b.no1;
    return res;
  }
} Tree[5001000];
void EXIT() {
  puts("NO");
  exit(0);
}
void modify(int t, int l, int r, int L, int R, int val) {
  if (L > r || l > R) return;
  if (L <= l && r <= R) {
    tree[t] = min(tree[t], val);
    return;
  }
  tree[t * 2] = min(tree[t * 2], tree[t]);
  tree[t * 2 + 1] = min(tree[t * 2 + 1], tree[t]);
  tree[t] = 0x3f3f3f3f;
  int mid = (l + r) / 2;
  modify(t * 2, l, mid, L, R, val);
  modify(t * 2 + 1, mid + 1, r, L, R, val);
}
int query(int t, int l, int r, int pos) {
  if (l == r) return tree[t];
  int mid = (l + r) / 2;
  tree[t * 2] = min(tree[t * 2], tree[t]);
  tree[t * 2 + 1] = min(tree[t * 2 + 1], tree[t]);
  tree[t] = 0x3f3f3f3f;
  if (pos <= mid)
    return query(t * 2, l, mid, pos);
  else
    return query(t * 2 + 1, mid + 1, r, pos);
}
void build(int t, int l, int r) {
  if (l == r) {
    Tree[t].val = a[l];
    Tree[t].cnt = 1;
    Tree[t].no1 = l;
    Tree[t].no2 = 0;
    return;
  }
  int mid = (l + r) / 2;
  build(t * 2, l, mid);
  build(t * 2 + 1, mid + 1, r);
  Tree[t] = Tree[t * 2] + Tree[t * 2 + 1];
}
void modify(int t, int l, int r, int pos, int val) {
  if (l == r) {
    Tree[t].val = val;
    Tree[t].cnt = 1;
    Tree[t].no1 = l;
    Tree[t].no2 = 0;
    return;
  }
  int mid = (l + r) / 2;
  if (pos <= mid)
    modify(t * 2, l, mid, pos, val);
  else
    modify(t * 2 + 1, mid + 1, r, pos, val);
  Tree[t] = Tree[t * 2] + Tree[t * 2 + 1];
}
Segment_Tree query(int t, int l, int r, int L, int R) {
  if (L > r || l > R) return {0, 0, 0, 0};
  if (L <= l && r <= R) return Tree[t];
  int mid = (l + r) / 2;
  return query(t * 2, l, mid, L, R) + query(t * 2 + 1, mid + 1, r, L, R);
}
void doit(int &a) {
  if (a == 0 || a == 1) return;
  int i;
  for (i = 30; i >= 0; i--)
    if (a & (1 << i)) break;
  a = (1 << i) - 1;
}
int main() {
  scanf("%d%d", &n, &m);
  for (int i = 1; i <= n; i++) a[i] = -1;
  memset(tree, 0x3f, sizeof(tree));
  for (int i = 1; i <= m; i++) {
    scanf("%d", &q[i].opt);
    if (q[i].opt == 1) {
      scanf("%d%d%d", &q[i].x, &q[i].y, &q[i].z);
      modify(1, 1, n, q[i].x, q[i].y, q[i].z);
    } else {
      scanf("%d%d", &q[i].x, &q[i].y);
      if (a[q[i].x] == -1) a[q[i].x] = query(1, 1, n, q[i].x);
    }
  }
  for (int i = 1; i <= n; i++) {
    if (a[i] == -1) a[i] = query(1, 1, n, i);
    if (a[i] > N) a[i] = N + 1;
  }
  build(1, 1, n);
  for (int i = 1; i <= m; i++) {
    if (q[i].opt == 1) {
      int x = query(1, 1, n, q[i].x, q[i].y).val;
      if (q[i].z != x) EXIT();
    } else
      modify(1, 1, n, q[i].x, q[i].y);
  }
  puts("YES");
  for (int i = 1; i <= n; i++)
    if (a[i] > N) v.push_back(i);
  if (v.size() >= 2) {
    a[v[0]] = (1 << 29) - 1;
    a[v[1]] = (1 << 29);
    for (int i = 1; i <= n; i++)
      if (a[i] <= N)
        printf("%d ", a[i]);
      else
        printf("%d\n", N);
    return 0;
  }
  build(1, 1, n);
  for (int i = 1; i <= m; i++) {
    if (q[i].opt == 2) continue;
    Segment_Tree Q = query(1, 1, n, q[i].x, q[i].y);
    if (Q.cnt == 2) doit(a[Q.no1]);
    modify(1, 1, n, Q.no1, a[Q.no1]);
  }
  if (v.size() >= 1) {
    int pos = v[0], res = 0;
    for (int i = 1; i <= n; i++)
      if (i != pos) res |= a[i];
    int x = 0;
    for (int i = 29; i >= 0; i--)
      if (!(res & (1 << i)) && x + (1 << i) <= N) x |= 1 << i;
    a[pos] = x;
  }
  for (int i = 1; i <= n; i++)
    if (a[i] <= N)
      printf("%d ", a[i]);
    else
      printf("%d\n", N);
}
