import std.stdio, std.conv, std.string, std.array, std.range, std.algorithm, std.container;
import std.math, std.random, std.bigint, std.datetime, std.format;
bool DEBUG = 0; void log(A ...)(lazy A a){ if(DEBUG) print(a); }
void main(string[] args){ args ~= ["", ""]; string cmd = args[1]; if(cmd == "-debug") DEBUG = 1;
if(cmd == "-gen") gen; else if(cmd == "-jury") jury; else solve; } 
void print(){ writeln(""); } void print(T)(T t){ writeln(t); } void print(T, A ...)(T t, A a){ std.stdio.write(t, " "), print(a); }
string unsplit(T)(T xs){ return xs.array.to!(string[]).join(" "); }
string scan(){ static string[] ss; while(!ss.length) ss = readln.chomp.split; string res = ss[0]; ss.popFront; return res; }
T scan(T)(){ return scan.to!T; } T[] scan(T)(int n){ return n.iota.map!(i => scan!T()).array; }
T lowerTo(T)(ref T x, T y){ if(x > y) x = y; return x; } T raiseTo(T)(ref T x, T y){ if(x < y) x = y; return x; }

// ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- //
void gen(){}
void jury(){}
struct Int{ int l, r; } // l <= x < r
void solve(){
  int n = scan!int, q = scan!int;
  
  // next white stone
  auto holi = new SegTree!int(0, n, (int a, int b) => min(a, b), n - 1);
  auto vert = new SegTree!int(0, n, (int a, int b) => min(a, b), n - 1);
  holi.setValue(0, n, n - 1);
  vert.setValue(0, n, n - 1);
  
  long ans = (n - 2).to!long * (n - 2).to!long;
  foreach(_; 0 .. q){
    int c = scan!int, x = scan!int - 1;
    if(c == 1){ // comes from (1, x)
      ans -= vert.getValue(x).to!long - 1;
      if(x < holi.getValue(0)) holi.setValue(0, vert.getValue(x) + 1, x);
    }
    else{ // comes from (x, 1)
      ans -= holi.getValue(x).to!long - 1;
      if(x < vert.getValue(0)) vert.setValue(0, holi.getValue(x) + 1, x);
    }
  }
  
  ans.print;
}
  


/*
セグメント木 区分木 segtree segment tree
範囲に書き込み、1点で読み出す。
たとえば、 [l, r) の範囲に一律 x を足すクエリがくるなど。
※書き込み操作は、単位元みたいなものが存在しなければならない
（「すべての x に対して f(z, x) = f(x, z) = x」なる z ）
たとえば、+ や `max` など
引数
a, b　添字の範囲。 この意味はa から b - 1 までなので注意。
apply　(x, y) => すでに値 x があるところに y を加えるとどうなるか ※結合的な演算である必要がある
neutral　すべての x に対して apply(z, x) = apply(x, z) = x をみたす z
*/
class SegTree(T){
	T delegate(T, T) apply;
	T neutral;
	int a, b; // a <= x < b を担当する
	bool isTerminal;
	SegTree left, right; // 左の子(a <= x < c)、右の子(c <= x < b)
	T value;
	this(int a, int b, T delegate(T, T) apply, T neutral = T.init){
		this.a = a, this.b = b;
		this.apply = apply;
		this.neutral = neutral;
		this.value = neutral;
		if(b - a == 1){
			this.isTerminal = 1;
		}
		else{
			int c = (a + b) / 2;
			this.left = new SegTree(a, c, apply, neutral);
			this.right = new SegTree(c, b, apply, neutral);
		}
	}
	
	// [a, b) に値を設定する ※[a, b) はこのノードの担当区間とかぶっていなくてもよい
	void setValue(int a, int b, T value){
		if(b <= this.a || this.b <= a) return;
		if(a <= this.a && this.b <= b){
			this.value = apply(this.value, value);
		}
		else{
			divideValue();
			left.setValue(a, b, value);
			right.setValue(a, b, value);
		}
	}
	
	// 自分の持っていた値を子に引き継ぐ
	void divideValue(){
		left.value = apply(left.value, this.value);
		right.value = apply(right.value, this.value);
		this.value = neutral;
	}
	
	// iにおける値 ※iは必ずこのノードの担当区間内である前提
	T getValue(int i){
		assert(a <= i && i < b);
		if(this.isTerminal) return this.value;
		else divideValue();
		if(i < left.b) return left.getValue(i);
		else return right.getValue(i);
	}
	
	// 配列に変換したもの（デバッグ用）
	T[] array(){
		T[] res;
		foreach(i; a .. b) res ~= this.value;
		return res;
	}
	
	// 文字列に変換したもの（デバッグ用）
	override string toString(){
		if(this.isTerminal) return this.array.to!string;
		else{
			string[] l = left.toString.split("\n"), r = right.toString.split("\n");
			while(l.length < r.length) l ~= l[$ - 1].map!(x => " ").join;
			while(l.length > r.length) r ~= r[$ - 1].map!(x => " ").join;
			string[] res = [this.array.to!string];
			foreach(i; 0 .. l.length) res ~= l[i] ~ r[i];
			return res.join("\n");
		}
	}
}


Measured memory_bytes: 117712