#include<bits/stdc++.h>

using namespace std;

typedef long long int64;

struct DinamicBinaryIndexedTree
{
  map< int, int64 > data;

  int64 sum(int k)
  {
    int64 ret = 0;
    for(++k; k <= 100010; k += k & -k) {
      if(data.count(k)) ret += data[k];
    }
    return (ret);
  }

  void add(int k, int64 x)
  {
    for(++k; k > 0; k -= k & -k) {
      data[k] += x;
    }
  }

};

struct SegmentTree
{
  int sz;
  vector< DinamicBinaryIndexedTree > seg;

  SegmentTree(int n)
  {
    sz = 1;
    while(sz < n) sz <<= 1;
    seg.resize(2 * sz - 1);
  }

  void update(int x, int y, int64 z)
  {
    x += sz - 1;
    seg[x].add(y, z);
    while(x > 0) {
      x = (x - 1) >> 1;
      seg[x].add(y, z);
    }
  }

  int64 query(int a, int b, int k, int l, int r)
  {
    if(a >= r || b <= l) return (0);
    if(a <= l && r <= b) return (seg[k].sum(a));
    return (query(a, b, 2 * k + 1, l, (l + r) >> 1) + query(a, b, 2 * k + 2, (l + r) >> 1, r));
  }

  int64 query(int a, int b)
  {
    return (query(a, b, 0, 0, sz));
  }
};

int main()
{
  int N, M, A[100000];
  set< int > st[100001];

  scanf("%d %d", &N, &M);
  for(int i = 0; i < N; i++) {
    scanf("%d", &A[i]);
    st[A[i]].emplace(i);
  }
  for(int i = 1; i <= N; i++) st[i].emplace(N);

  SegmentTree seg(N + 1);
  for(int i = 0; i < N; i++) {
    auto it = st[A[i]].lower_bound(i);
    if(it != begin(st[A[i]])) seg.update(*it, *prev(it), *it - *prev(it));
  }

  for(int i = 0; i < M; i++) {
    int T, X, Y;
    scanf("%d %d %d", &T, &X, &Y);
    --X;
    if(T == 1) {
      auto it = st[A[X]].lower_bound(X);
      if(it != begin(st[A[X]])) seg.update(*it, *prev(it), *prev(it) - *it);
      seg.update(*next(it), *it, *it - *next(it));
      it = st[A[X]].erase(it);
      if(it != begin(st[A[X]])) seg.update(*it, *prev(it), *it - *prev(it));

      A[X] = Y;
      it = st[A[X]].lower_bound(X);
      if(it != begin(st[A[X]])) seg.update(*it, *prev(it), *prev(it) - *it);
      st[A[X]].emplace(X);
      it = st[A[X]].lower_bound(X);
      if(it != begin(st[A[X]])) seg.update(*it, *prev(it), *it - *prev(it));
      ++it;
      seg.update(*it, *prev(it), *it - *prev(it));
    } else {
      printf("%lld\n", seg.query(X, Y));
    }
  }
}


#include<bits/stdc++.h>

using namespace std;

typedef long long int64;

template< class T >
struct BinaryIndexedTree
{
  vector< T > data;

  BinaryIndexedTree(int sz)
  {
    data.assign(++sz, 0);
  }

  T sum(int k)
  {
    T ret = 0;
    for(++k; k < data.size(); k += k & -k) ret += data[k];
    return (ret);
  }

  void add(int k, T x)
  {
    k = min(k, (int)data.size() - 2);
    for(++k; k > 0; k -= k & -k) data[k] += x;
  }
};

struct SegmentTree
{
  int sz;
  vector< vector< int > > nums;
  vector< BinaryIndexedTree< int64 > > seg;

  SegmentTree(vector< vector< int > > sg)
  {
    sz = 1;
    while(sz < sg.size()) sz <<= 1;
    nums.resize(2 * sz - 1);
    for(int i = 0; i < sg.size(); i++) nums[sz + i - 1] = sg[i];
    for(int i = sz - 2; i >= 0; i--) {
      nums[i].resize(nums[2 * i + 1].size() + nums[2 * i + 2].size());
      merge(begin(nums[2 * i + 1]), end(nums[2 * i + 1]), begin(nums[2 * i + 2]), end(nums[2 * i + 2]), begin(nums[i]));
    }
    for(int i = 0; i < 2 * sz - 1; i++) seg.push_back(BinaryIndexedTree< int64 >(nums[i].size()));
  }

  void update(int x, int y, int64 z)
  {
    x += sz - 1;
    seg[x].add(lower_bound(begin(nums[x]), end(nums[x]), y) - begin(nums[x]), z);
    while(x > 0) {
      x = (x - 1) >> 1;
      seg[x].add(lower_bound(begin(nums[x]), end(nums[x]), y) - begin(nums[x]), z);
    }
  }

  int64 query(int a, int b, int k, int l, int r)
  {
    if(a >= r || b <= l) return (0);
    if(a <= l && r <= b) return (seg[k].sum(lower_bound(begin(nums[k]), end(nums[k]), a) - begin(nums[k])));
    return (query(a, b, 2 * k + 1, l, (l + r) >> 1) + query(a, b, 2 * k + 2, (l + r) >> 1, r));
  }

  int64 query(int a, int b)
  {
    return (query(a, b, 0, 0, sz));
  }
};

int main()
{
  int N, M, A[100000];
  set< int > st[100001];
  int T[100000], X[100000], Y[100000];

  scanf("%d %d", &N, &M);
  for(int i = 0; i < N; i++) {
    scanf("%d", &A[i]);
    st[A[i]].emplace(i);
  }
  for(int i = 1; i <= N; i++) st[i].emplace(N);

  vector< tuple< int, int, int > > qs[100001];
  vector< vector< int > > sg(N + 1);
  auto push = [&](int idx, int a, int b, int c)
  {
    sg[a].push_back(b);
    qs[idx].emplace_back(a, b, c);
  };

  for(int i = 0; i < N; i++) {
    auto it = st[A[i]].lower_bound(i);
    if(it != begin(st[A[i]])) push(100000, *it, *prev(it), *it - *prev(it));
  }

  for(int i = 0; i < M; i++) {
    scanf("%d %d %d", &T[i], &X[i], &Y[i]);
    --X[i];
    if(T[i] == 1) {
      auto it = st[A[X[i]]].lower_bound(X[i]);
      if(it != begin(st[A[X[i]]])) push(i, *it, *prev(it), *prev(it) - *it);
      push(i, *next(it), *it, *it - *next(it));
      it = st[A[X[i]]].erase(it);
      if(it != begin(st[A[X[i]]])) push(i, *it, *prev(it), *it - *prev(it));

      A[X[i]] = Y[i];
      it = st[A[X[i]]].lower_bound(X[i]);
      if(it != begin(st[A[X[i]]])) push(i, *it, *prev(it), *prev(it) - *it);
      st[A[X[i]]].emplace(X[i]);
      it = st[A[X[i]]].lower_bound(X[i]);
      if(it != begin(st[A[X[i]]])) push(i, *it, *prev(it), *it - *prev(it));
      ++it;
      push(i, *it, *prev(it), *it - *prev(it));
    }
  }

  for(int i = 0; i < N; i++) {
    sort(begin(sg[i]), end(sg[i]));
    sg[i].erase(unique(begin(sg[i]), end(sg[i])), end(sg[i]));
  }

  SegmentTree seg(sg);
  auto dodo = [&](int idx)
  {
    for(auto& p : qs[idx]) {
      int a, b, c;
      tie(a, b, c) = p;
      seg.update(a, b, c);
    }
  };
  dodo(100000);

  for(int i = 0; i < M; i++) {
    if(T[i] == 1) dodo(i);
    else printf("%lld\n", seg.query(X[i], Y[i]));
  }
}


#include<bits/stdc++.h>

using namespace std;

typedef long long int64;

struct DinamicBinaryIndexedTree
{
  map< int, int64 > data;

  int64 sum(int k)
  {
    int64 ret = 0;
    for(++k; k <= 100010; k += k & -k) {
      if(data.count(k)) ret += data[k];
    }
    return (ret);
  }

  void add(int k, int64 x)
  {
    for(++k; k > 0; k -= k & -k) {
      data[k] += x;
    }
  }

};

struct SegmentTree
{
  int sz;
  vector< DinamicBinaryIndexedTree > seg;

  SegmentTree(int n)
  {
    sz = 1;
    while(sz < n) sz <<= 1;
    seg.resize(2 * sz - 1);
  }

  void update(int x, int y, int64 z)
  {
    x += sz - 1;
    seg[x].add(y, z);
    while(x > 0) {
      x = (x - 1) >> 1;
      seg[x].add(y, z);
    }
  }

  int64 query(int a, int b, int k, int l, int r)
  {
    if(a >= r || b <= l) return (0);
    if(a <= l && r <= b) return (seg[k].sum(a));
    return (query(a, b, 2 * k + 1, l, (l + r) >> 1) + query(a, b, 2 * k + 2, (l + r) >> 1, r));
  }

  int64 query(int a, int b)
  {
    return (query(a, b, 0, 0, sz));
  }
};

int main()
{
  int N, M, A[100000];
  set< int > st[100001];

  scanf("%d %d", &N, &M);
  for(int i = 0; i < N; i++) {
    scanf("%d", &A[i]);
    st[A[i]].emplace(i);
  }
  for(int i = 1; i <= N; i++) st[i].emplace(N);

  SegmentTree seg(N + 1);
  for(int i = 0; i < N; i++) {
    auto it = st[A[i]].lower_bound(i);
    if(it != begin(st[A[i]])) seg.update(*it, *prev(it), *it - *prev(it));
  }

  for(int i = 0; i < M; i++) {
    int T, X, Y;
    scanf("%d %d %d", &T, &X, &Y);
    --X;
    if(T == 1) {
      auto it = st[A[X]].lower_bound(X);
      if(it != begin(st[A[X]])) seg.update(*it, *prev(it), *prev(it) - *it);
      seg.update(*next(it), *it, *it - *next(it));
      it = st[A[X]].erase(it);
      seg.update(*it, *prev(it), *it - *prev(it));

      A[X] = Y;
      it = st[A[X]].lower_bound(X);
      if(it != begin(st[A[X]])) seg.update(*it, *prev(it), *prev(it) - *it);
      st[A[X]].emplace(X);
      it = st[A[X]].lower_bound(X);
      if(it != begin(st[A[X]])) seg.update(*it, *prev(it), *it - *prev(it));
      ++it;
      seg.update(*it, *prev(it), *it - *prev(it));
    } else {
      printf("%lld\n", seg.query(X, Y));
    }
  }
}


#include <bits/stdc++.h>

using namespace std;

struct Mo_3D
{
  vector< int > left, right, index, order;
  vector< bool > v;
  int width;
  int nl, nr, time, ptr;

  Mo_3D(int n) : width((int) pow(n, 2.0 / 3.0)), nl(0), nr(0), ptr(0), time(0), v(n) {}

  void insert(int idx, int l, int r) /* [l, r) */
  {
    left.push_back(l);
    right.push_back(r);
    index.push_back(idx);
  }

  void build()
  {
    order.resize(left.size());
    iota(begin(order), end(order), 0);
    sort(begin(order), end(order), [&](int a, int b)
    {
      if(left[a] / width != left[b] / width) return left[a] < left[b];
      if(right[a] / width != right[b] / width) return bool((right[a] < right[b]) ^ (left[a] / width % 2));
      return bool((index[a] < index[b]) ^ (right[a] / width % 2));
    });
  }

  int process()
  {
    if(ptr == order.size()) return (-1);
    const auto id = order[ptr];
    while(time < index[id]) addquery(time++);
    while(time > index[id]) delquery(--time);
    while(nl > left[id]) distribute(--nl);
    while(nr < right[id]) distribute(nr++);
    while(nl < left[id]) distribute(nl++);
    while(nr > right[id]) distribute(--nr);
    return (index[order[ptr++]]);
  }

  inline void distribute(int idx)
  {
    v[idx].flip();
    if(v[idx]) add(idx);
    else del(idx);
  }

  void add(int idx);

  void del(int idx);

  void addquery(int idx);

  void delquery(int idx);
};

typedef long long int64;

int N, M, A[100000], buff[100000];

set< int > line[100001];
int init_pv[100000], init_nt[100000];
int X[100000], Y[100000], ZZ[100000], Z[100000];

vector< pair< int, int > > update_pv[100000], update_nt[100000];
int first[100001], last[100001];

int64 ret[100001], ans;

void Mo_3D::add(int idx)
{
  if(first[A[idx]] == -1 || (idx < first[A[idx]])) {
    if(first[A[idx]] != -1) ans += first[A[idx]];
    ans -= idx;
    first[A[idx]] = idx;
  }
  if(last[A[idx]] == -1 || (idx > last[A[idx]])) {
    if(last[A[idx]] != -1) ans -= last[A[idx]];
    ans += idx;
    last[A[idx]] = idx;
  }
}

void Mo_3D::del(int idx)
{
  if(first[A[idx]] == idx) {
    ans += idx;
    if(nl <= init_pv[idx] && init_pv[idx] < nr) {
      first[A[idx]] = init_pv[idx];
      ans -= first[A[idx]];
    } else if(nl <= init_nt[idx] && init_nt[idx] < nr) {
      first[A[idx]] = init_nt[idx];
      ans -= first[A[idx]];
    } else {
      first[A[idx]] = -1;
    }
  }
  if(last[A[idx]] == idx) {
    ans -= idx;
    if(nl <= init_nt[idx] && init_nt[idx] < nr) {
      last[A[idx]] = init_nt[idx];
      ans += last[A[idx]];
    } else if(nl <= init_pv[idx] && init_pv[idx] < nr) {
      last[A[idx]] = init_pv[idx];
      ans += last[A[idx]];
    } else {
      last[A[idx]] = -1;
    }
  }
}

void Mo_3D::addquery(int idx)
{
  if(X[idx] == 2) return;

  if(v[Y[idx]]) {
    distribute(Y[idx]);
    for(auto &q : update_pv[idx]) swap(init_pv[q.first], q.second);
    for(auto &q : update_nt[idx]) swap(init_nt[q.first], q.second);
    swap(A[Y[idx]], Z[idx]);
    distribute(Y[idx]);
  } else {
    for(auto &q : update_pv[idx]) swap(init_pv[q.first], q.second);
    for(auto &q : update_nt[idx]) swap(init_nt[q.first], q.second);
    swap(A[Y[idx]], Z[idx]);
  }
}

void Mo_3D::delquery(int idx)
{
  addquery(idx);
}

int main()
{
  scanf("%d %d", &N, &M);
  for(int i = 0; i < N; i++) {
    scanf("%d", &A[i]);
    line[A[i]].emplace(i);
    buff[i] = A[i];
  }
  for(int i = 0; i < M; i++) {
    scanf("%d %d %d", &X[i], &Y[i], &Z[i]);
    --Y[i];
  }

  memset(init_pv, -1, sizeof(init_pv));
  memset(init_nt, -1, sizeof(init_nt));
  for(int i = 0; i < N; i++) {
    auto p = line[A[i]].lower_bound(i);
    if(p != begin(line[A[i]])) init_pv[i] = *prev(p);
    if(next(p) != end(line[A[i]])) init_nt[i] = *next(p);
  }

  Mo_3D mo(N);
  for(int i = 0; i < M; i++) {

    if(X[i] == 1) {
      {
        auto p = line[A[Y[i]]].lower_bound(Y[i]);
        const int pv = (p != begin(line[A[Y[i]]]) ? *prev(p) : -1);
        const int nt = (next(p) != end(line[A[Y[i]]]) ? *next(p) : -1);
        if(~pv) update_nt[i].emplace_back(pv, nt);
        if(~nt) update_pv[i].emplace_back(nt, pv);
        line[A[Y[i]]].erase(Y[i]);
      }
      {
        A[Y[i]] = Z[i];
        line[A[Y[i]]].emplace(Y[i]);
        auto p = line[A[Y[i]]].lower_bound(Y[i]);
        const int pv = (p != begin(line[A[Y[i]]]) ? *prev(p) : -1);
        const int nt = (next(p) != end(line[A[Y[i]]]) ? *next(p) : -1);
        if(~pv) update_nt[i].emplace_back(pv, Y[i]);
        if(~nt) update_pv[i].emplace_back(nt, Y[i]);
        update_pv[i].emplace_back(Y[i], pv);
        update_nt[i].emplace_back(Y[i], nt);
      }
    } else {
      mo.insert(i, Y[i], Z[i]);
    }
  }

  mo.build();

  int id;
  memset(first, -1, sizeof(first));
  memset(last, -1, sizeof(last));
  for(int i = 0; i < N; i++) A[i] = buff[i];
  while((id = mo.process()) != -1) ret[id] = ans;

  for(int i = 0; i < M; i++) {
    if(X[i] == 2) printf("%lld\n", ret[i]);
  }
}


#include <bits/stdc++.h>

using namespace std;

struct Mo_3D
{
  vector< int > left, right, index, order;
  vector< bool > v;
  int width;
  int nl, nr, time, ptr;

  Mo_3D(int n) : width((int) pow(n, 2.0 / 3.0)), nl(0), nr(0), ptr(0), time(0), v(n) {}

  void insert(int idx, int l, int r) /* [l, r) */
  {
    left.push_back(l);
    right.push_back(r);
    index.push_back(idx);
  }

  void build()
  {
    order.resize(left.size());
    iota(begin(order), end(order), 0);
    sort(begin(order), end(order), [&](int a, int b)
    {
      if(left[a] / width != left[b] / width) return left[a] < left[b];
      if(right[a] / width != right[b] / width) return bool((right[a] < right[b]) ^ (left[a] / width % 2));
      return bool((index[a] < index[b]) ^ (right[a] / width % 2));
    });
  }

  int process()
  {
    if(ptr == order.size()) return (-1);
    const auto id = order[ptr];
    while(time < index[id]) addquery(time++);
    while(time > index[id]) delquery(--time);
    while(nl > left[id]) distribute(--nl);
    while(nr < right[id]) distribute(nr++);
    while(nl < left[id]) distribute(nl++);
    while(nr > right[id]) distribute(--nr);
    return (index[order[ptr++]]);
  }

  inline void distribute(int idx)
  {
    v[idx].flip();
    if(v[idx]) add(idx);
    else del(idx);
  }

  void add(int idx);

  void del(int idx);

  void addquery(int idx);

  void delquery(int idx);
};

typedef long long int64;

int N, M, A[100000], buff[100000];

set< int > line[100001];
int init_pv[100000], init_nt[100000];
int X[100000], Y[100000], Z[100000];

vector< pair< int, int > > update_pv[100000], update_nt[100000];
int first[100001], last[100001];

int64 ret[100001], ans;

void Mo_3D::add(int idx)
{
  if(first[A[idx]] == -1 || (idx < first[A[idx]])) {
    if(first[A[idx]] != -1) ans += first[A[idx]];
    ans -= idx;
    first[A[idx]] = idx;
  }
  if(last[A[idx]] == -1 || (idx > last[A[idx]])) {
    if(last[A[idx]] != -1) ans -= last[A[idx]];
    ans += idx;
    last[A[idx]] = idx;
  }
}

void Mo_3D::del(int idx)
{
  if(first[A[idx]] == idx) {
    ans += idx;
    if(init_nt[idx] != -1 && init_nt[idx] < nr) {
      first[A[idx]] = init_nt[idx];
      ans -= first[A[idx]];
    } else {
      first[A[idx]] = -1;
    }
  }
  if(last[A[idx]] == idx) {
    ans -= idx;
    if(init_pv[idx] != nl && nl <= init_pv[idx]) {
      last[A[idx]] = init_pv[idx];
      ans += last[A[idx]];
    } else {
      last[A[idx]] = -1;
    }
  }
}

void Mo_3D::addquery(int idx)
{
  if(X[idx] == 2) return;

  if(v[Y[idx]]) {
    distribute(Y[idx]);
    for(auto &q : update_pv[idx]) swap(init_pv[q.first], q.second);
    for(auto &q : update_nt[idx]) swap(init_nt[q.first], q.second);
    swap(A[Y[idx]], Z[idx]);
    distribute(Y[idx]);
  } else {
    for(auto &q : update_pv[idx]) swap(init_pv[q.first], q.second);
    for(auto &q : update_nt[idx]) swap(init_nt[q.first], q.second);
    swap(A[Y[idx]], Z[idx]);
  }
}

void Mo_3D::delquery(int idx)
{
  addquery(idx);
}

int main()
{
  scanf("%d %d", &N, &M);
  for(int i = 0; i < N; i++) {
    scanf("%d", &A[i]);
    line[A[i]].emplace(i);
    buff[i] = A[i];
  }
  for(int i = 0; i < M; i++) {
    scanf("%d %d %d", &X[i], &Y[i], &Z[i]);
    --Y[i];
  }

  memset(init_pv, -1, sizeof(init_pv));
  memset(init_nt, -1, sizeof(init_nt));
  for(int i = 0; i < N; i++) {
    auto p = line[A[i]].lower_bound(i);
    if(p != begin(line[A[i]])) init_pv[i] = *prev(p);
    if(next(p) != end(line[A[i]])) init_nt[i] = *next(p);
  }

  Mo_3D mo(N);
  for(int i = 0; i < M; i++) {

    if(X[i] == 1) {
      {
        auto p = line[A[Y[i]]].lower_bound(Y[i]);
        const int pv = (p != begin(line[A[Y[i]]]) ? *prev(p) : -1);
        const int nt = (next(p) != end(line[A[Y[i]]]) ? *next(p) : -1);
        if(~pv) update_nt[i].emplace_back(pv, nt);
        if(~nt) update_pv[i].emplace_back(nt, pv);
        line[A[Y[i]]].erase(Y[i]);
      }
      {
        A[Y[i]] = Z[i];
        line[A[Y[i]]].emplace(Y[i]);
        auto p = line[A[Y[i]]].lower_bound(Y[i]);
        const int pv = (p != begin(line[A[Y[i]]]) ? *prev(p) : -1);
        const int nt = (next(p) != end(line[A[Y[i]]]) ? *next(p) : -1);
        if(~pv) update_nt[i].emplace_back(pv, Y[i]);
        if(~nt) update_pv[i].emplace_back(nt, Y[i]);
        update_pv[i].emplace_back(Y[i], pv);
        update_nt[i].emplace_back(Y[i], nt);
      }
    } else {
      mo.insert(i, Y[i], Z[i]);
    }
  }

  mo.build();

  int id;
  memset(first, -1, sizeof(first));
  memset(last, -1, sizeof(last));
  for(int i = 0; i < N; i++) A[i] = buff[i];
  while((id = mo.process()) != -1) ret[id] = ans;

  for(int i = 0; i < M; i++) {
    if(X[i] == 2) printf("%lld\n", ret[i]);
  }
}


#include <bits/stdc++.h>

using namespace std;

struct Mo_3D
{
  vector< int > left, right, index, order;
  vector< bool > v;
  int width;
  int nl, nr, time, ptr;

  Mo_3D(int n) : width((int) pow(n, 2.0 / 3.0)), nl(0), nr(0), ptr(0), time(0), v(n) {}

  void insert(int idx, int l, int r) /* [l, r) */
  {
    left.push_back(l);
    right.push_back(r);
    index.push_back(idx);
  }

  void build()
  {
    order.resize(left.size());
    iota(begin(order), end(order), 0);
    sort(begin(order), end(order), [&](int a, int b)
    {
      if(left[a] / width != left[b] / width) return left[a] < left[b];
      if(right[a] / width != right[b] / width) return bool((right[a] < right[b]) ^ (left[a] / width % 2));
      return bool((index[a] < index[b]) ^ (right[a] / width % 2));
    });
  }

  int process()
  {
    if(ptr == order.size()) return (-1);
    const auto id = order[ptr];
    while(time < index[id]) addquery(time++);
    while(time > index[id]) delquery(--time);
    while(nl > left[id]) distribute(--nl);
    while(nr < right[id]) distribute(nr++);
    while(nl < left[id]) distribute(nl++);
    while(nr > right[id]) distribute(--nr);
    return (index[order[ptr++]]);
  }

  inline void distribute(int idx)
  {
    v[idx].flip();
    if(v[idx]) add(idx);
    else del(idx);
  }

  void add(int idx);

  void del(int idx);

  void addquery(int idx);

  void delquery(int idx);
};

typedef long long int64;

int N, M, A[100000], buff[100000];

set< int > line[100001];
int init_pv[100000], init_nt[100000];
int X[100000], Y[100000], Z[100000];

vector< pair< int, int > > update_pv[100000], update_nt[100000];
int first[100001], last[100001];

int64 ret[100001], ans;

void Mo_3D::add(int idx)
{
  if(first[A[idx]] == -1 || (first[A[idx]] != -1 && idx < first[A[idx]])) {
    if(first[A[idx]] != -1) ans += first[A[idx]];
    ans -= idx;
    first[A[idx]] = idx;
  }
  if(last[A[idx]] == -1 || (last[A[idx]] != -1 && idx > last[A[idx]])) {
    if(last[A[idx]] != -1) ans -= last[A[idx]];
    ans += idx;
    last[A[idx]] = idx;
  }
}

void Mo_3D::del(int idx)
{
  if(first[A[idx]] == idx) {
    ans += idx;
    if(init_pv[idx] != -1 && nl <= init_pv[idx]) {
      first[A[idx]] = init_pv[idx];
      ans -= first[A[idx]];
    } else if(init_nt[idx] != -1 && init_nt[idx] < nr) {
      first[A[idx]] = init_nt[idx];
      ans -= first[A[idx]];
    } else {
      first[A[idx]] = -1;
    }
  }
  if(last[A[idx]] == idx) {
    ans -= idx;
    if(init_nt[idx] != -1 && init_nt[idx] < nr) {
      last[A[idx]] = init_nt[idx];
      ans += last[A[idx]];
    } else if(init_pv[idx] != -1 && nl <= init_pv[idx]) {
      last[A[idx]] = init_pv[idx];
      ans += last[A[idx]];
    } else {
      last[A[idx]] = -1;
    }
  }
}

void Mo_3D::addquery(int idx)
{
  if(X[idx] == 2) return;

  if(v[Y[idx]]) {
    distribute(Y[idx]);
    for(auto &q : update_pv[idx]) swap(init_pv[q.first], q.second);
    for(auto &q : update_nt[idx]) swap(init_nt[q.first], q.second);
    swap(A[Y[idx]], Z[idx]);
    distribute(Y[idx]);
  } else {
    for(auto &q : update_pv[idx]) swap(init_pv[q.first], q.second);
    for(auto &q : update_nt[idx]) swap(init_nt[q.first], q.second);
    swap(A[Y[idx]], Z[idx]);
  }
}

void Mo_3D::delquery(int idx)
{
  addquery(idx);
}

int main()
{
  scanf("%d %d", &N, &M);
  for(int i = 0; i < N; i++) {
    scanf("%d", &A[i]);
    line[A[i]].emplace(i);
    buff[i] = A[i];
  }
  for(int i = 0; i < M; i++) {
    scanf("%d %d %d", &X[i], &Y[i], &Z[i]);
    --Y[i];
  }

  memset(init_pv, -1, sizeof(init_pv));
  memset(init_nt, -1, sizeof(init_nt));
  for(int i = 0; i < N; i++) {
    auto p = line[A[i]].lower_bound(i);
    if(p != begin(line[A[i]])) init_pv[i] = *prev(p);
    if(next(p) != end(line[A[i]])) init_nt[i] = *next(p);
  }

  Mo_3D mo(N);
  for(int i = 0; i < M; i++) {

    if(X[i] == 1) {
      {
        auto p = line[A[Y[i]]].lower_bound(Y[i]);
        const int pv = (p != begin(line[A[Y[i]]]) ? *prev(p) : -1);
        const int nt = (next(p) != end(line[A[Y[i]]]) ? *next(p) : -1);
        if(~pv) update_nt[i].emplace_back(pv, nt);
        if(~nt) update_pv[i].emplace_back(nt, pv);
        line[A[Y[i]]].erase(Y[i]);
      }
      {
        A[Y[i]] = Z[i];
        line[A[Y[i]]].emplace(Y[i]);
        auto p = line[A[Y[i]]].lower_bound(Y[i]);
        const int pv = (p != begin(line[A[Y[i]]]) ? *prev(p) : -1);
        const int nt = (next(p) != end(line[A[Y[i]]]) ? *next(p) : -1);
        if(~pv) update_nt[i].emplace_back(pv, Y[i]);
        if(~nt) update_pv[i].emplace_back(nt, Y[i]);
        update_pv[i].emplace_back(Y[i], pv);
        update_nt[i].emplace_back(Y[i], nt);
      }
    } else {
      mo.insert(i, Y[i], Z[i]);
    }
  }

  mo.build();

  int id;
  memset(first, -1, sizeof(first));
  memset(last, -1, sizeof(last));
  for(int i = 0; i < N; i++) A[i] = buff[i];
  while((id = mo.process()) != -1) ret[id] = ans;

  for(int i = 0; i < M; i++) {
    if(X[i] == 2) printf("%lld\n", ret[i]);
  }
}


#include<bits/stdc++.h>

using namespace std;

typedef long long int64;

struct DinamicBinaryIndexedTree
{
  map< int, int64 > data;

  int64 sum(int k)
  {
    int64 ret = 0;
    for(++k; k <= 100010; k += k & -k) {
      if(data.count(k)) ret += data[k];
    }
    return (ret);
  }

  void add(int k, int64 x)
  {
    for(++k; k > 0; k -= k & -k) {
      data[k] += x;
      if(data[k] == 0) data.erase(k);
    }
  }

};

struct SegmentTree
{
  int sz;
  vector< DinamicBinaryIndexedTree > seg;

  SegmentTree(int n)
  {
    sz = 1;
    while(sz < n) sz <<= 1;
    seg.resize(2 * sz - 1);
  }

  void update(int x, int y, int64 z)
  {
    x += sz - 1;
    seg[x].add(y, z);
    while(x > 0) {
      x = (x - 1) >> 1;
      seg[x].add(y, z);
    }
  }

  int64 query(int a, int b, int k, int l, int r)
  {
    if(a >= r || b <= l) return (0);
    if(a <= l && r <= b) return (seg[k].sum(a));
    return (query(a, b, 2 * k + 1, l, (l + r) >> 1) + query(a, b, 2 * k + 2, (l + r) >> 1, r));
  }

  int64 query(int a, int b)
  {
    return (query(a, b, 0, 0, sz));
  }
};

int main()
{
  int N, M, A[100000];
  set< int > st[100001];

  scanf("%d %d", &N, &M);
  for(int i = 0; i < N; i++) {
    scanf("%d", &A[i]);
    st[A[i]].emplace(i);
  }
  for(int i = 1; i <= N; i++) st[i].emplace(N);

  SegmentTree seg(N + 1);
  for(int i = 0; i < N; i++) {
    auto it = st[A[i]].lower_bound(i);
    if(it != begin(st[A[i]])) seg.update(*it, *prev(it), *it - *prev(it));
  }

  for(int i = 0; i < M; i++) {
    int T, X, Y;
    scanf("%d %d %d", &T, &X, &Y);
    --X;
    if(T == 1) {
      auto it = st[A[X]].lower_bound(X);
      if(it != begin(st[A[X]])) seg.update(*it, *prev(it), *prev(it) - *it);
      seg.update(*next(it), *it, *it - *next(it));
      it = st[A[X]].erase(it);
      if(it != begin(st[A[X]])) seg.update(*it, *prev(it), *it - *prev(it));

      A[X] = Y;
      it = st[A[X]].lower_bound(X);
      if(it != begin(st[A[X]])) seg.update(*it, *prev(it), *prev(it) - *it);
      st[A[X]].emplace(X);
      it = st[A[X]].lower_bound(X);
      if(it != begin(st[A[X]])) seg.update(*it, *prev(it), *it - *prev(it));
      ++it;
      seg.update(*it, *prev(it), *it - *prev(it));
    } else {
      printf("%lld\n", seg.query(X, Y));
    }
  }
}


/*input
7 5
1 3 2 1 4 2 3
1 1 4
2 2 3
1 1 7
2 4 5
1 1 7
*/
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
#define REP(i,j,k)     for(int i = j ; i < k ; ++i)
#define RREP(i,j,k)    for(int i = j ; i >=k ; --i)
#define A    first
#define B    second
#define mp   make_pair
#define pb   emplace_back
#define PII pair<int , int>
#define MEM(i,j)   memset(i , j , sizeof i)
#define ALL(i)     i.begin() , i.end()
#define DBGG(i,j)     cout << i << " " << j << endl
#define DB4(i,j,k,l)  cout << i << " " << j << " " << k << " " << l << endl
#define IOS cin.tie(0) , cout.sync_with_stdio(0)
#define endl "\n"
///------------------------------------------------------------
#define int unsigned
#define MAX 100900
#define INF 0x3f3f3f3f
#define mid (l + r >> 1)

int bit[MAX];
void update(int from , int val){
	for(int i = from + 10 ; i < MAX ; i += i & -i)
		bit[i] += val;
}
int query(int from){
	int cnt = 0;
	for(int i = from + 10 ; i > 0 ; i -= i & -i)
		cnt += bit[i];
	return cnt;
}
int query(int l , int r){ return query(r) - query(l - 1); }
struct node{ int ty , l , r , id; };
vector<node> sol;
set<int> cc[MAX];
int n , m , x[MAX] , ans[MAX] , qpo = 0;
int cmp(node a , node b){ return a.l < b.l; }
void solve(int l , int r){
	if(l == r) return ;
	else {
		vector<node> lth , rth;
		REP(i , l , mid + 0 + 1) if(sol[i].ty == 0) lth.pb(sol[i]);
		REP(i , mid + 1 , r + 1) if(sol[i].ty == 1) rth.pb(sol[i]);
		sort(ALL(lth) , cmp) , sort(ALL(rth) , cmp);
		// for(auto to : lth) DB4(to.ty , to.l , to.r , to.id);
		// 	cout << endl;
		// for(auto to : rth) DB4(to.ty , to.l , to.r , to.id);
		int pos = 0;
		for(auto now : rth){
			while(pos < lth.size() && lth[pos].l <= now.l)
				update(lth[pos].r , lth[pos].id) , pos ++;
			ans[now.id] += query(now.r , n + 1);
		}
		while(pos - 1 >= 0) pos -- , update(lth[pos].r , -lth[pos].id);
		solve(l , mid + 0);
		solve(mid + 1 , r);
	}
}
void add(int id , int pre , int val){
	sol.pb(node{0 , id , pre , (id - pre) * val});
}
int32_t main(){
	IOS;
	cin >> n >> m;
	REP(i , 1 , n + 1) cin >> x[i];
	REP(i , 1 , n + 1) cc[i].insert(0);
	REP(i , 1 , n + 1){
		int pre = *prev(cc[x[i]].end());
		sol.pb(node{0 , i , pre , i - pre});
		cc[x[i]].insert(i);
	}
	REP(i , 1 , n + 1) cc[i].insert(n + 1);
	REP(i , 1 , m + 1){
		int ty , a , b;
		cin >> ty >> a >> b;
		if(ty == 1){
			auto now = cc[x[a]].find(a);
			add(*now , *prev(now) , -1);
			add(*next(now) , *now , -1);
			add(*next(now) , *prev(now) , 1);
			cc[x[a]].erase(a);
			x[a] = b;
			cc[x[a]].insert(a);
			now = cc[x[a]].find(a);
			add(*now , *prev(now) , 1);
			add(*next(now) , *now , 1);
			add(*next(now) , *prev(now) , -1);
		}
		if(ty == 2) sol.pb(node{1 , b , a , ++qpo});
	}

	solve(0 , sol.size());
	REP(i , 1 , qpo + 1) cout << ans[i] << endl;
    return 0;
}


#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
#define REP(i,j,k)     for(int i = j ; i < k ; ++i)
#define RREP(i,j,k)    for(int i = j ; i >=k ; --i)
#define A    first
#define B    second
#define pb   push_back
#define mp   make_pair
#define PII pair<int , int>
#define MEM(i,j)   memset(i , j , sizeof i)
#define ALL(i)     i.begin() , i.end()
// #define DBGG(i,j)     cout << i << " " << j << endl
// #define DB4(i,j,k,l)  cout << i << " " << j << " " << k << " " << l << endl
#define IOS cin.tie() , cout.sync_with_stdio(0)
// #define endl "\n"
///------------------------------------------------------------
#define MAX 100900
#define INF 0x3f3f3f3f

int n , m , Q , M , N;
long long theans;
set<int> cc[MAX];
int x[MAX] , a[MAX][3] , p[MAX][3] , sol[MAX];
long long ans[MAX];
void ADD(int now , int id){
    if(now == -1) now = x[id];
    if(cc[now].size() <= 1){
        cc[now].insert(id);
        if(cc[now].size() == 2){
            auto fr = cc[now].begin();
            auto ba = cc[now].end(); ba--;
            theans += *ba - *fr;
        }
    }
    else {
        auto fr = cc[now].begin();
        auto ba = cc[now].end(); ba--;
        theans -= *ba - *fr;
        cc[now].insert(id);
        theans += max(*ba , id) - min(*fr , id);
    }
}
void STR(int now , int id){
    if(now == -1) now = x[id];
    if(cc[now].size() == 1) cc[now].erase(id);
    else {
        auto fr = cc[now].begin();
        auto ba = cc[now].end(); ba--;
        theans -= *ba - *fr;
        cc[now].erase(id);
        fr = cc[now].begin();
        ba = cc[now].end(); ba--;
        theans += *ba - *fr;
    }
}
void ADDT(int L , int R , int T){
    int id = p[T][0];
    x[id] = p[T][2];
    if(L <= id && id <= R) STR(p[T][1] , id) , ADD(p[T][2] , id);
}
void STRT(int L , int R , int T){
    int id = p[T][0];
    x[id] = p[T][1];
    if(L <= id && id <= R) STR(p[T][2] , id) , ADD(p[T][1] , id);
}
int cmp(int x , int y){
    if(a[x][0] / N != a[y][0] / N) return a[x][0] / N < a[y][0] / N;
    if(a[x][1] / N != a[y][1] / N) return a[x][1] / N < a[y][1] / N;
    return a[x][2] < a[y][2];
}
void solve(){
    N = pow(n / log(n) , 2.0 / 3.0) , N = max(N , 1);
    REP(i , 0 , Q) sol[i] = i;
    sort(sol , sol + Q , cmp);
    int L = 0 , R = -1 , T = 0;
    REP(i , 0 , Q){
        int ll = a[sol[i]][0] , rr = a[sol[i]][1] , tt = a[sol[i]][2];
        while(T < tt) ADDT(L , R , ++T);
        while(tt < T) STRT(L , R , T--);
        while(R < rr) ADD(-1 , ++R);
        while(ll < L) ADD(-1 , --L);
        while(rr < R) STR(-1 , R--);
        while(L < ll) STR(-1 , L++);
        ans[sol[i]] = theans;
    }
}
int qqq[MAX];
int32_t main(){
    IOS;
    cin >> n >> m;
    REP(i , 0 , n) cin >> x[i] , qqq[i] = x[i];

    REP(i , 0 , m){
        int q , w , e;
        cin >> q >> w >> e;
        if(q == 2) a[Q][0] = w - 1 , a[Q][1] = e - 1 , a[Q][2] = M , Q ++;
        else{
            M ++ , p[M][0] = w - 1 , p[M][1] = qqq[w - 1] , p[M][2] = e;
            qqq[w - 1] = e;
        }
    }

    solve();
    REP(i , 0 , Q) cout << ans[i] << endl;

    return 0;
}


/*input
7 5
1 3 2 1 4 2 3
1 1 4
2 2 3
1 1 7
2 4 5
1 1 7
*/
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
#define REP(i,j,k)     for(int i = j ; i < k ; ++i)
#define RREP(i,j,k)    for(int i = j ; i >=k ; --i)
#define A    first
#define B    second
#define mp   make_pair
#define pb   emplace_back
#define PII pair<int , int>
#define MEM(i,j)   memset(i , j , sizeof i)
#define ALL(i)     i.begin() , i.end()
#define DBGG(i,j)     cout << i << " " << j << endl
#define DB4(i,j,k,l)  cout << i << " " << j << " " << k << " " << l << endl
#define IOS cin.tie(0) , cout.sync_with_stdio(0)
#define endl "\n"
///------------------------------------------------------------
#define int long long
#define MAX 100900
#define INF 0x3f3f3f3f
#define mid (l + r >> 1)

int bit[MAX];
void update(int from , int val){
	for(int i = from + 10 ; i < MAX ; i += i & -i)
		bit[i] += val;
}
int query(int from){
	int cnt = 0;
	for(int i = from + 10 ; i > 0 ; i -= i & -i)
		cnt += bit[i];
	return cnt;
}
int query(int l , int r){ return query(r) - query(l - 1); }
struct node{ int ty , l , r , id; };
vector<node> sol;
set<int> cc[MAX];
int n , m , x[MAX] , ans[MAX] , qpo = 0;
int cmp(node a , node b){ return a.l < b.l; }
void solve(int l , int r){
	if(l == r) return ;
	else {
		vector<node> lth , rth;
		REP(i , l , mid + 0 + 1) if(sol[i].ty == 0) lth.pb(sol[i]);
		REP(i , mid + 1 , r + 1) if(sol[i].ty == 1) rth.pb(sol[i]);
		sort(ALL(lth) , cmp) , sort(ALL(rth) , cmp);
		// for(auto to : lth) DB4(to.ty , to.l , to.r , to.id);
		// 	cout << endl;
		// for(auto to : rth) DB4(to.ty , to.l , to.r , to.id);
		int pos = 0;
		for(auto now : rth){
			while(pos < lth.size() && lth[pos].l <= now.l)
				update(lth[pos].r , lth[pos].id) , pos ++;
			ans[now.id] += query(now.r , n + 1);
		}
		while(pos - 1 >= 0) pos -- , update(lth[pos].r , -lth[pos].id);
		solve(l , mid + 0);
		solve(mid + 1 , r);
	}
}
void add(int id , int pre , int val){
	sol.pb(node{0 , id , pre , (id - pre) * val});
}
int32_t main(){
	IOS;
	cin >> n >> m;
	REP(i , 1 , n + 1) cin >> x[i];
	REP(i , 1 , n + 1) cc[i].insert(0);
	REP(i , 1 , n + 1){
		int pre = *prev(cc[x[i]].end());
		sol.pb(node{0 , i , pre , i - pre});
		cc[x[i]].insert(i);
	}
	REP(i , 1 , n + 1) cc[i].insert(n + 1);
	REP(i , 1 , m + 1){
		int ty , a , b;
		cin >> ty >> a >> b;
		if(ty == 1){
			auto now = cc[x[a]].find(a);
			add(*now , *prev(now) , -1);
			add(*next(now) , *now , -1);
			add(*next(now) , *prev(now) , 1);
			cc[x[a]].erase(a);
			x[a] = b;
			cc[x[a]].insert(a);
			now = cc[x[a]].find(a);
			add(*now , *prev(now) , 1);
			add(*next(now) , *now , 1);
			add(*next(now) , *prev(now) , -1);
		}
		if(ty == 2) sol.pb(node{1 , b , a , ++qpo});
	}

	solve(0 , sol.size());
	REP(i , 1 , qpo + 1) cout << ans[i] << endl;
    return 0;
}


#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
#define REP(i,j,k)     for(int i = j ; i < k ; ++i)
#define RREP(i,j,k)    for(int i = j ; i >=k ; --i)
#define A    first
#define B    second
#define pb   push_back
#define mp   make_pair
#define PII pair<int , int>
#define MEM(i,j)   memset(i , j , sizeof i)
#define ALL(i)     i.begin() , i.end()
#define DBGG(i,j)     cout << i << " " << j << endl
#define DB4(i,j,k,l)  cout << i << " " << j << " " << k << " " << l << endl
#define IOS cin.tie() , cout.sync_with_stdio(0)
// #define endl "\n"
///------------------------------------------------------------
#define MAX 100900
#define INF 0x3f3f3f3f

int n , m , Q , M , N;
long long theans;
set<int> cc[MAX];
int x[MAX] , a[MAX][3] , p[MAX][3] , sol[MAX];
long long ans[MAX];
void ADD(int now , int id){
    if(now == -1) now = x[id];
    if(cc[now].size() <= 1){
        cc[now].insert(id);
        if(cc[now].size() == 2){
            auto fr = cc[now].begin();
            auto ba = cc[now].end(); ba--;
            theans += *ba - *fr;
        }
    }
    else {
        auto fr = cc[now].begin();
        auto ba = cc[now].end(); ba--;
        theans -= *ba - *fr;
        cc[now].insert(id);
        fr = cc[now].begin();
        ba = cc[now].end(); ba--;
        theans += *ba - *fr;
    }
}
void STR(int now , int id){
    if(now == -1) now = x[id];
    if(cc[now].size() == 1) cc[now].erase(id);
    else {
        auto fr = cc[now].begin();
        auto ba = cc[now].end(); ba--;
        theans -= *ba - *fr;
        cc[now].erase(id);
        fr = cc[now].begin();
        ba = cc[now].end(); ba--;
        theans += *ba - *fr;
    }
}
void ADDT(int L , int R , int T){
    int id = p[T][0];
    x[id] = p[T][2];
    if(L <= id && id <= R) STR(p[T][1] , id) , ADD(p[T][2] , id);
}
void STRT(int L , int R , int T){
    int id = p[T][0];
    x[id] = p[T][1];
    if(L <= id && id <= R) STR(p[T][2] , id) , ADD(p[T][1] , id);
}
int cmp(int x , int y){
    if(a[x][0] / N != a[y][0] / N) return a[x][0] / N < a[y][0] / N;
    if(a[x][1] / N != a[y][1] / N) return a[x][1] / N < a[y][1] / N;
    return a[x][2] < a[y][2];
}
void solve(){
    N = pow(n , 2.0 / 3.0) , N = max(N , 1);
    REP(i , 0 , Q) sol[i] = i;
    sort(sol , sol + Q , cmp);
    int L = 0 , R = -1 , T = 0;
    REP(i , 0 , Q){
        int ll = a[sol[i]][0] , rr = a[sol[i]][1] , tt = a[sol[i]][2];
        while(T < tt) ADDT(L , R , ++T);
        while(tt < T) STRT(L , R , T--);
        while(R < rr) ADD(-1 , ++R);
        while(ll < L) ADD(-1 , --L);
        while(rr < R) STR(-1 , R--);
        while(L < ll) STR(-1 , L++);
        ans[sol[i]] = theans;
    }
}
int qqq[MAX];
int32_t main(){
    IOS;
    cin >> n >> m;
    REP(i , 0 , n) cin >> x[i] , qqq[i] = x[i];

    REP(i , 0 , m){
        int q , w , e;
        cin >> q >> w >> e;
        if(q == 2) a[Q][0] = w - 1 , a[Q][1] = e - 1 , a[Q][2] = M , Q ++;
        else{
            M ++ , p[M][0] = w - 1 , p[M][1] = qqq[w - 1] , p[M][2] = e;
            qqq[w - 1] = e;
        }
    }

    solve();
    REP(i , 0 , Q) cout << ans[i] << endl;

    return 0;
}


/*input
7 5
1 3 2 1 4 2 3
1 1 4
2 2 3
1 1 7
2 4 5
1 1 7
*/
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
#define REP(i,j,k)     for(int i = j ; i < k ; ++i)
#define RREP(i,j,k)    for(int i = j ; i >=k ; --i)
#define A    first
#define B    second
#define mp   make_pair
#define pb   emplace_back
#define PII pair<int , int>
#define MEM(i,j)   memset(i , j , sizeof i)
#define ALL(i)     i.begin() , i.end()
#define DBGG(i,j)     cout << i << " " << j << endl
#define DB4(i,j,k,l)  cout << i << " " << j << " " << k << " " << l << endl
#define IOS cin.tie(0) , cout.sync_with_stdio(0)
#define endl "\n"
///------------------------------------------------------------
#define MAX 100900
#define INF 0x3f3f3f3f
#define mid (l + r >> 1)

int bit[MAX];
void update(int from , int val){
	for(int i = from + 10 ; i < MAX ; i += i & -i)
		bit[i] += val;
}
int query(int from){
	int cnt = 0;
	for(int i = from + 10 ; i > 0 ; i -= i & -i)
		cnt += bit[i];
	return cnt;
}
int query(int l , int r){ return query(r) - query(l - 1); }
struct node{ int ty , l , r , id; };
vector<node> sol;
set<int> cc[MAX];
int n , m , x[MAX] , ans[MAX] , qpo = 0;
int cmp(node a , node b){ return a.l < b.l; }
void solve(int l , int r){
	if(l == r) return ;
	else {
		vector<node> lth , rth;
		REP(i , l , mid + 0 + 1) if(sol[i].ty == 0) lth.pb(sol[i]);
		REP(i , mid + 1 , r + 1) if(sol[i].ty == 1) rth.pb(sol[i]);
		sort(ALL(lth) , cmp) , sort(ALL(rth) , cmp);
		int pos = 0;
		for(auto now : rth){
			while(pos < lth.size() && lth[pos].l <= now.l)
				update(lth[pos].r , lth[pos].id) , pos ++;
			ans[now.id] += query(now.r , n + 1);
		}
		while(pos - 1 >= 0) pos -- , update(lth[pos].r , -lth[pos].id);
		solve(l , mid + 0);
		solve(mid + 1 , r);
	}
}
void add(int id , int pre , int val){
	sol.pb(node{0 , id , pre , (id - pre) * val});
}
int32_t main(){
	IOS;
	cin >> n >> m;
	REP(i , 1 , n + 1) cin >> x[i];
	REP(i , 1 , n + 1) cc[i].insert(0);
	REP(i , 1 , n + 1){
		int pre = *prev(cc[x[i]].end());
		sol.pb(node{0 , i , pre , i - pre});
		cc[x[i]].insert(i);
	}
	REP(i , 1 , n + 1) cc[i].insert(n + 1);
	REP(i , 1 , m + 1){
		int ty , a , b;
		cin >> ty >> a >> b;
		if(ty == 1){
			auto now = cc[x[a]].find(a);
			add(*now , *prev(now) , -1);
			add(*next(now) , *now , -1);
			add(*next(now) , *prev(now) , 1);
			cc[x[a]].erase(a);
			x[a] = b;
			cc[x[a]].insert(a);
			now = cc[x[a]].find(a);
			add(*now , *prev(now) , 1);
			add(*next(now) , *now , 1);
			add(*next(now) , *prev(now) , -1);
		}
		if(ty == 2) sol.pb(node{1 , b , a , ++qpo});
	}
	solve(0 , sol.size());
	REP(i , 1 , qpo + 1) cout << ans[i] << endl;
    return 0;
}


/*input
7 5
1 3 2 1 4 2 3
1 1 4
2 2 3
1 1 7
2 4 5
1 1 7
*/
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
#define REP(i,j,k)     for(int i = j ; i < k ; ++i)
#define RREP(i,j,k)    for(int i = j ; i >=k ; --i)
#define A    first
#define B    second
#define mp   make_pair
#define pb   emplace_back
#define PII pair<int , int>
#define MEM(i,j)   memset(i , j , sizeof i)
#define ALL(i)     i.begin() , i.end()
#define DBGG(i,j)     cout << i << " " << j << endl
#define DB4(i,j,k,l)  cout << i << " " << j << " " << k << " " << l << endl
#define IOS cin.tie(0) , cout.sync_with_stdio(0)
#define endl "\n"
///------------------------------------------------------------
#define int long long
#define MAX 100900
#define INF 0x3f3f3f3f
#define mid (l + r >> 1)

int bit[MAX];
void update(int from , int val){
	for(int i = from + 10 ; i < MAX ; i += i & -i)
		bit[i] += val;
}
int query(int from){
	int cnt = 0;
	for(int i = from + 10 ; i > 0 ; i -= i & -i)
		cnt += bit[i];
	return cnt;
}
int query(int l , int r){ return query(r) - query(l - 1); }
struct node{ int ty , l , r , id; };
vector<node> sol;
set<int> cc[MAX];
int n , m , x[MAX] , ans[MAX] , qpo = 0;
int cmp(node a , node b){ return a.l < b.l; }
void solve(int l , int r){
	if(l == r) return ;
	else {
		vector<node> lth , rth;
		REP(i , l , mid + 0 + 1) if(sol[i].ty == 0) lth.pb(sol[i]);
		REP(i , mid + 1 , r + 1) if(sol[i].ty == 1) rth.pb(sol[i]);
		sort(ALL(lth) , cmp) , sort(ALL(rth) , cmp);
		int pos = 0;
		for(auto now : rth){
			while(pos < lth.size() && lth[pos].l <= now.l)
				update(lth[pos].r , lth[pos].id) , pos ++;
			ans[now.id] += query(now.r , n + 1);
		}
		while(pos - 1 >= 0) pos -- , update(lth[pos].r , -lth[pos].id);
		solve(l , mid + 0);
		solve(mid + 1 , r);
	}
}
void add(int id , int pre , int val){
	sol.pb(node{0 , id , pre , (id - pre) * val});
}
int32_t main(){
	IOS;
	cin >> n >> m;
	REP(i , 1 , n + 1) cin >> x[i];
	REP(i , 1 , n + 1) cc[i].insert(0);
	REP(i , 1 , n + 1){
		int pre = *prev(cc[x[i]].end());
		sol.pb(node{0 , i , pre , i - pre});
		cc[x[i]].insert(i);
	}
	REP(i , 1 , n + 1) cc[i].insert(n + 1);
	REP(i , 1 , m + 1){
		int ty , a , b;
		cin >> ty >> a >> b;
		if(ty == 1){
			auto now = cc[x[a]].find(a);
			add(*now , *prev(now) , -1);
			add(*next(now) , *now , -1);
			add(*next(now) , *prev(now) , 1);
			cc[x[a]].erase(a);
			x[a] = b;
			cc[x[a]].insert(a);
			now = cc[x[a]].find(a);
			add(*now , *prev(now) , 1);
			add(*next(now) , *now , 1);
			add(*next(now) , *prev(now) , -1);
		}
		if(ty == 2) sol.pb(node{1 , b , a , ++qpo});
	}
	solve(0 , sol.size());
	REP(i , 1 , qpo + 1) cout << ans[i] << endl;
    return 0;
}


/*input
7 5
1 3 2 1 4 2 3
1 1 4
2 2 3
1 1 7
2 4 5
1 1 7
*/
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
#define REP(i,j,k)     for(int i = j ; i < k ; ++i)
#define RREP(i,j,k)    for(int i = j ; i >=k ; --i)
#define A    first
#define B    second
#define mp   make_pair
#define pb   emplace_back
#define PII pair<int , int>
#define MEM(i,j)   memset(i , j , sizeof i)
#define ALL(i)     i.begin() , i.end()
#define DBGG(i,j)     cout << i << " " << j << endl
#define DB4(i,j,k,l)  cout << i << " " << j << " " << k << " " << l << endl
#define IOS cin.tie(0) , cout.sync_with_stdio(0)
#define endl "\n"
///------------------------------------------------------------
#define MAX 100900
#define INF 0x3f3f3f3f
#define mid (l + r >> 1)

int bit[MAX];
void update(int from , int val){
	for(int i = from + 10 ; i < MAX ; i += i & -i)
		bit[i] += val;
}
int query(int from){
	int cnt = 0;
	for(int i = from + 10 ; i > 0 ; i -= i & -i)
		cnt += bit[i];
	return cnt;
}
int query(int l , int r){ return query(r) - query(l - 1); }
struct node{ int ty , l , r , id; };
vector<node> sol;
set<int> cc[MAX];
int n , m , x[MAX] , ans[MAX] , qpo = 0;
int cmp(node a , node b){ return a.l < b.l; }
void solve(int l , int r){
	if(l == r) return ;
	else {
		vector<node> lth , rth;
		REP(i , l , mid + 0 + 1) if(sol[i].ty == 0) lth.pb(sol[i]);
		REP(i , mid + 1 , r + 1) if(sol[i].ty == 1) rth.pb(sol[i]);
		sort(ALL(lth) , cmp) , sort(ALL(rth) , cmp);
		// for(auto to : lth) DB4(to.ty , to.l , to.r , to.id);
		// 	cout << endl;
		// for(auto to : rth) DB4(to.ty , to.l , to.r , to.id);
		int pos = 0;
		for(auto now : rth){
			while(pos < lth.size() && lth[pos].l <= now.l)
				update(lth[pos].r , lth[pos].id) , pos ++;
			ans[now.id] += query(now.r , n + 1);
		}
		while(pos - 1 >= 0) pos -- , update(lth[pos].r , -lth[pos].id);
		solve(l , mid + 0);
		solve(mid + 1 , r);
	}
}
void add(int id , int pre , int val){
	sol.pb(node{0 , id , pre , (id - pre) * val});
}
int32_t main(){
	IOS;
	cin >> n >> m;
	REP(i , 1 , n + 1) cin >> x[i];
	REP(i , 1 , n + 1) cc[i].insert(0);
	REP(i , 1 , n + 1){
		int pre = *prev(cc[x[i]].end());
		sol.pb(node{0 , i , pre , i - pre});
		cc[x[i]].insert(i);
	}
	REP(i , 1 , n + 1) cc[i].insert(n + 1);
	REP(i , 1 , m + 1){
		int ty , a , b;
		cin >> ty >> a >> b;
		if(ty == 1){
			auto now = cc[x[a]].find(a);
			add(*now , *prev(now) , -1);
			add(*next(now) , *now , -1);
			add(*next(now) , *prev(now) , 1);
			cc[x[a]].erase(a);
			x[a] = b;
			cc[x[a]].insert(a);
			now = cc[x[a]].find(a);
			add(*now , *prev(now) , 1);
			add(*next(now) , *now , 1);
			add(*next(now) , *prev(now) , -1);
		}
		if(ty == 2) sol.pb(node{1 , b , a , ++qpo});
	}

	solve(0 , sol.size());
	REP(i , 1 , qpo + 1) cout << ans[i] << endl;
    return 0;
}


#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
#define REP(i,j,k)     for(int i = j ; i < k ; ++i)
#define RREP(i,j,k)    for(int i = j ; i >=k ; --i)
#define A    first
#define B    second
#define pb   push_back
#define mp   make_pair
#define PII pair<int , int>
#define MEM(i,j)   memset(i , j , sizeof i)
#define ALL(i)     i.begin() , i.end()
// #define DBGG(i,j)     cout << i << " " << j << endl
// #define DB4(i,j,k,l)  cout << i << " " << j << " " << k << " " << l << endl
#define IOS cin.tie() , cout.sync_with_stdio(0)
// #define endl "\n"
///------------------------------------------------------------
#define MAX 100900
#define INF 0x3f3f3f3f

int n , m , Q , M , N;
long long theans;
set<int> cc[MAX];
int x[MAX] , a[MAX][3] , p[MAX][3] , sol[MAX];
long long ans[MAX];
void ADD(int now , int id){
    if(now == -1) now = x[id];
    if(cc[now].size() <= 1){
        cc[now].insert(id);
        if(cc[now].size() == 2){
            auto fr = cc[now].begin();
            auto ba = cc[now].end(); ba--;
            theans += *ba - *fr;
        }
    }
    else {
        auto fr = cc[now].begin();
        auto ba = cc[now].end(); ba--;
        theans -= *ba - *fr;
        cc[now].insert(id);
        theans += max(*ba , id) - min(*fr , id);
    }
}
void STR(int now , int id){
    if(now == -1) now = x[id];
    if(cc[now].size() == 1) cc[now].erase(id);
    else {
        auto fr = cc[now].begin();
        auto ba = cc[now].end(); ba--;
        theans -= *ba - *fr;
        cc[now].erase(id);
        fr = cc[now].begin();
        ba = cc[now].end(); ba--;
        theans += *ba - *fr;
    }
}
void ADDT(int L , int R , int T){
    int id = p[T][0];
    x[id] = p[T][2];
    if(L <= id && id <= R) STR(p[T][1] , id) , ADD(p[T][2] , id);
}
void STRT(int L , int R , int T){
    int id = p[T][0];
    x[id] = p[T][1];
    if(L <= id && id <= R) STR(p[T][2] , id) , ADD(p[T][1] , id);
}
int cmp(int x , int y){
    if(a[x][0] / N != a[y][0] / N) return a[x][0] / N < a[y][0] / N;
    if(a[x][1] / N != a[y][1] / N) return a[x][1] / N < a[y][1] / N;
    return a[x][2] < a[y][2];
}
void solve(){
    N = pow(n * log(n) , 2.0 / 3.0) , N = max(N , 1);
    REP(i , 0 , Q) sol[i] = i;
    sort(sol , sol + Q , cmp);
    int L = 0 , R = -1 , T = 0;
    REP(i , 0 , Q){
        int ll = a[sol[i]][0] , rr = a[sol[i]][1] , tt = a[sol[i]][2];
        while(T < tt) ADDT(L , R , ++T);
        while(tt < T) STRT(L , R , T--);
        while(R < rr) ADD(-1 , ++R);
        while(ll < L) ADD(-1 , --L);
        while(rr < R) STR(-1 , R--);
        while(L < ll) STR(-1 , L++);
        ans[sol[i]] = theans;
    }
}
int qqq[MAX];
int32_t main(){
    IOS;
    cin >> n >> m;
    REP(i , 0 , n) cin >> x[i] , qqq[i] = x[i];

    REP(i , 0 , m){
        int q , w , e;
        cin >> q >> w >> e;
        if(q == 2) a[Q][0] = w - 1 , a[Q][1] = e - 1 , a[Q][2] = M , Q ++;
        else{
            M ++ , p[M][0] = w - 1 , p[M][1] = qqq[w - 1] , p[M][2] = e;
            qqq[w - 1] = e;
        }
    }

    solve();
    REP(i , 0 , Q) cout << ans[i] << endl;

    return 0;
}


#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

#ifdef BTK
#define DEBUG if(1)
#else
#define DEBUG if(0)
#define CIN_ONLY if(0)
struct cww{cww(){
    CIN_ONLY{
        ios::sync_with_stdio(false);cin.tie(0);
    }
}}star;

#endif

#define fin "\n"
#define FOR(i,bg,ed) for(int i=(bg);i<(ed);i++)
#define REP(i,n) FOR(i,0,n)
#define ALL(v) (v).begin(),(v).end()
#define fi first
#define se second
#define pb push_back
#define REC(ret, ...) std::function<ret (__VA_ARGS__)>
template <typename T>inline bool chmin(T &l,T r)
{bool a=l>r;if(a)l=r;return a;}
template <typename T>inline bool chmax(T &l,T r)
{bool a=l<r;if(a)l=r;return a;}
template <typename T>
istream& operator>>(istream &is,vector<T> &v){
    for(auto &it:v)is>>it;
    return is;
}

namespace AA{
    using Key = int;
    using Val = LL;
    constexpr int preffered_size = 2123456;
    struct node{
        int level,size;
        node* par;
        node* c[2];
        Key key,lb,ub;
        Val val,sum;
    };
    using link = node*;
        inline void update(const link v){
            //key
            v->size = v->c[0]->size + v->c[1]->size + !!v->level;
            v->lb   = v->c[0]->level ? v->c[0]->lb : v->key;
            v->ub   = v->c[1]->level ? v->c[1]->ub : v->key;
            //val
            v->sum = v->val;
            v->sum += v->c[0]->level ? v->c[0]->sum : 0;
            v->sum += v->c[1]->level ? v->c[1]->sum : 0;
        }

    //-memory pool--------------------------
    namespace pool{
        node mem[preffered_size];
        struct stack{
            node* stk[preffered_size];
            int size;
            stack(){
                size = 0;
                REP(i,preffered_size)
                    stk[i]=(mem+i);
            }
            inline node* const pop(){
                return stk[size++];
            }
            inline void push(node* const p){
                stk[--size]=p;
            }
        }stk;
        inline link get(){
            link ret = stk.pop();
            ret->par = NULL;
            return ret;
        }
        inline void trash(link v){
            stk.push(v);
        }

    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    inline const link leaf(const link lf = pool::get()){
        lf->level = 0;
        lf->size = 0;
        lf->c[0] = NULL;
        lf->c[1] = NULL;
        return lf;
    }
    inline bool isleaf(const link x){
        return !x->level;
    }
    inline const link assign(const link v,Key key,Val val){
        v->level = 1;
        v->key   = v->lb = v->ub = key;
        v->val   = val;
        v->c[0]  = leaf();v->c[0]->par = v;
        v->c[1]  = leaf();v->c[1]->par = v;
        update(v);
        return v;
    }

    struct tree{
        link root;
        inline const link skew(const link v){
            const link l = v->c[0];
            if(v->level == l->level){
                v->c[0] = l->c[1];
                l->c[1] = v;
                v->c[0]->par = v;
                l->par = v->par;
                v->par = l;
                if(!!l->par){
                    const link p = l->par;
                    p->c[p->key < l->key] = l;
                }
                update(v);
                update(l);
                return l;
            }
            else{
                update(v);
                return v;
            }
        }
        inline const link split(const link v){
            const link r  = v->c[1];
            const link rr = r->c[1];
            if(v->level == r->level
               && v->level == rr->level){
                v->c[1] = r->c[0];
                r->c[0] = v;
                v->c[1]->par = v;
                r->par = v->par;
                v->par = r;
                r->level ++;
                if(!!r->par){
                    const link p = r->par;
                    p->c[p->key < r->key] = r;
                }
                update(v);
                update(r);
                return r;
            }
            else{
                update(v);
                return v;
            }
        }

        inline const link modify(const link v){
            int level = min(v->c[0]->level,v->c[1]->level) + 1;
            chmin(v->level,level) && chmin(v->c[1]->level,level);
            return v;
        }

        inline bool insert(Key key,Val val){
            link v = root;
            while(v->level){
                if(v->key == key){
                    v->val = val;
                    return false;
                }
                v = v->c[v->key < key];
            }

            v = assign(v,key,val);

            while(!!v->par){
                v = v->par;
                v = skew(v);
                v = split(v);
            }
            root = v;
            return true;
        }

        inline bool erase(Key key){
            link v = root;
            while(!v->level||v->key!=key){
                if(!v->level)return false;
                v = v->c[v->key < key];
            }
            while(v->level){
                const link u = v->c[!v->c[0]->level];
                v->key = u->key;
                v->val = u->val;
                v = u;
            }

            v = v->par;
            pool::trash(v->c[0]);
            pool::trash(v->c[1]);

            v = leaf(v);

            while(!!v->par){
                v = v->par;
                v = modify(v);
                v = skew(v);
                link r = v->c[1];
                if(r->level){
                    r = skew(r);
                    const link rr = r->c[1];
                    if(rr->level){
                        skew(rr);
                    }
                }
                v = split(v);
                r = v->c[1];
                if(r->level){
                    split(r);
                }
            }
            root = v;
            return true;
        }

        inline Val get(Key b){
            link v = root;
            Val ret = 0;
            while(!isleaf(v)&& b >= v->lb){
                if(v->key <= b){
                    ret += v->val;
                    if(!isleaf(v->c[0]))ret += v->c[0]->sum;
                    v = v->c[1];
                }
                else{
                    v = v->c[0];
                }
            }
            return ret;
        }
        tree(){
            root = leaf();
        }
    };
}

typedef LL A;
typedef AA::tree B;
struct node{
    B b;
};
A mg(node& v,int key){
    return v.b.get(key);
}
A mg(A l,A r){
    return l+r;
}
namespace ST{
    node mem[1][512345];
    int cnt=0;
    node* get(){return mem[cnt++];}
}
struct seg_tree{
    int size;
    node *seg;
    void init(int l,int r,int k=0){
        auto &v=seg[k];
        v.b=B();
        if(r-l==1){
            //�t�̎��̏���
        }
        else if(r-l>1){
            int m=(l+r)/2;
            init(l,m,k*2+1);init(m,r,k*2+2);
        }
    }
    seg_tree(int n){
        size=1;
        while(size<n)size*=2;
        seg=ST::get();
        init(0,size);
    }
#define LQ a,b,k*2+1,l,m
#define RQ a,b,k*2+2,m,r
    A get(int a,int b,int k,int l,int r){
        if(a<=l){
            return mg(seg[k],b-1);
        }
        int m=(l+r)/2;
        bool ll=!(m<=a);
        bool rr=!(r<=a);
        A ret;
        if(ll&&rr)ret=mg(get(LQ),get(RQ));
        else if(ll)ret=get(LQ);
        else ret=get(RQ);
        return ret;
    }
    A get(int a,int b){return get(a,b,0,0,size);}
    void ins(int a,int b,int k,int l,int r,LL key,LL val){
        if(r<=a||b<=l)return;
        seg[k].b.insert(key,val);
        if(a<=l&&r<=b){

        }
        else{
            int m=(l+r)/2;
            ins(LQ,key,val);
            ins(RQ,key,val);
        }
    }
    void ins(int a,int b,LL val){
        ins(a,a+1,0,0,size,b-1,val);
    }
    void del(int a,int b,int k,int l,int r,LL key){
        if(r<=a||b<=l)return;
        seg[k].b.erase(key);
        if(a<=l&&r<=b){

        }
        else{
            int m=(l+r)/2;
            del(LQ,key);
            del(RQ,key);
        }
    }
    void del(int a,int b){
        del(a,a+1,0,0,size,b-1);
    }

};

int main(){
    int n,q;
    scanf("%d%d",&n,&q);
    vector<int> v(n);
    vector<set<int>> x(n);
    seg_tree seg(n+10);
    REP(i,n){
        int a;
        scanf("%d",&a);
        a--;
        v[i]=a;
        x[a].insert(i);
    }
    REP(i,n){
        int prv=-1;
        for(auto &now:x[i]){
            if(prv>=0){
                int len = now-prv;
                seg.ins(prv,now+1,len);
            }
            prv=now;
        }
    }

    REP(i,q){
        int type;
        scanf("%d",&type);
        if(type==2){
            int l,r;
            scanf("%d%d",&l,&r);
            printf("%lld\n",seg.get(l-1,r));
        }
        else{
            int p,val;
            scanf("%d%d",&p,&val);
            p--;
            val--;
            if(v[p]==val)continue;
            {//erase
                auto& td=x[v[p]];
                {
                    auto ym=td.find(p);
                    if(ym!=td.begin()){
                        auto yb=ym;yb--;
                        seg.del(*yb,(*ym)+1);
                    }
                    {
                        auto ye=ym;ye++;
                        if(ye!=td.end()){
                            seg.del(*ym,(*ye)+1);

                            if(ym!=td.begin()){
                                auto yb=ym;yb--;
                                seg.ins(*yb,(*ye)+1,(*ye-(*yb)));

                            }
                        }
                    }
                }
                td.erase(p);
            }

            v[p]=val;
            {//insert
                auto& td=x[val];
                td.insert(p);
                {
                    auto ym=td.find(p);
                    if(ym!=td.begin()){
                        auto yb=ym;yb--;
                        seg.ins(*yb,(*ym)+1,*ym-*yb);
                    }
                    {
                        auto ye=ym;ye++;
                        if(ye!=td.end()){

                            if(ym!=td.begin()){
                                auto yb=ym;yb--;
                                seg.del(*yb,(*ye)+1);
                            }

                            seg.ins(*ym,(*ye)+1,*ye-*ym);
                        }
                    }
                }
            }
        }
    }
    return 0;
}


#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

#ifdef BTK
#define DEBUG if(1)
#else
#define DEBUG if(0)
#define CIN_ONLY if(0)
struct cww{cww(){
    CIN_ONLY{
        ios::sync_with_stdio(false);cin.tie(0);
    }
}}star;

#endif

#define fin "\n"
#define FOR(i,bg,ed) for(int i=(bg);i<(ed);i++)
#define REP(i,n) FOR(i,0,n)
#define ALL(v) (v).begin(),(v).end()
#define fi first
#define se second
#define pb push_back
#define REC(ret, ...) std::function<ret (__VA_ARGS__)>
template <typename T>inline bool chmin(T &l,T r)
{bool a=l>r;if(a)l=r;return a;}
template <typename T>inline bool chmax(T &l,T r)
{bool a=l<r;if(a)l=r;return a;}
template <typename T>
istream& operator>>(istream &is,vector<T> &v){
    for(auto &it:v)is>>it;
    return is;
}

namespace AA{
    using Key = int;
    using Val = LL;
    constexpr int preffered_size = 1123456;
    struct node{
        int level,size;
        weak_ptr<node> par;
        shared_ptr<node> c[2];
        Key key,lb,ub;
        Val val,sum;
    };
    using link = shared_ptr<node>;
    using clink = const shared_ptr<node>;
        inline void update(const link v){
            //key
            v->size = v->c[0]->size + v->c[1]->size + !!v->level;
            v->lb   = v->c[0]->level ? v->c[0]->lb : v->key;
            v->ub   = v->c[1]->level ? v->c[1]->ub : v->key;
            //val
            v->sum = v->val;
            v->sum += v->c[0]->level ? v->c[0]->sum : 0;
            v->sum += v->c[1]->level ? v->c[1]->sum : 0;
        }

    //-memory pool--------------------------
    namespace pool{
        node mem[preffered_size];
        struct stack{
            node* stk[preffered_size];
            int size;
            stack(){
                size = 0;
                REP(i,preffered_size)
                    stk[i]=(mem+i);
            }
            inline node* const pop(){
                return stk[size++];
            }
            inline void push(node* const p){
                stk[--size]=p;
            }
        }stk;
        inline node* const allocater(){
            return stk.pop();
        }
        inline void deleter(node* const p){
            stk.push(p);
        }
        inline link get(){
            return move(link(allocater(),deleter));
        }
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    inline const link leaf(const link lf = pool::get()){
        lf->level = 0;
        lf->size = 0;
        lf->c[0] = nullptr;
        lf->c[1] = nullptr;
        return move(lf);
    }
    inline bool isleaf(const link x){
        return !x->level;
    }
    inline const link assign(const link v,Key key,Val val){
        v->level = 1;
        v->key   = v->lb = v->ub = key;
        v->val   = val;
        v->c[0]  = leaf();v->c[0]->par = v;
        v->c[1]  = leaf();v->c[1]->par = v;
        update(v);
        return move(v);
    }

    struct tree{
        link root;
        inline const link skew(const link v){
            const link l = v->c[0];
            if(v->level == l->level){
                v->c[0] = l->c[1];
                l->c[1] = v;
                v->c[0]->par = v;
                l->par = v->par;
                v->par = l;
                if(!l->par.expired()){
                    const link p = l->par.lock();
                    p->c[p->key < l->key] = l;
                }
                update(v);
                update(l);
                return move(l);
            }
            else{
                update(v);
                return move(v);
            }
        }
        inline const link split(const link v){
            const link r  = v->c[1];
            const link rr = r->c[1];
            if(v->level == r->level
               && v->level == rr->level){
                v->c[1] = r->c[0];
                r->c[0] = v;
                v->c[1]->par = v;
                r->par = v->par;
                v->par = r;
                r->level ++;
                if(!r->par.expired()){
                    const link p = r->par.lock();
                    p->c[p->key < r->key] = r;
                }
                update(v);
                update(r);
                return move(r);
            }
            else{
                update(v);
                return move(v);
            }
        }
        inline const link modify(const link v){
            int level = min(v->c[0]->level,v->c[1]->level) + 1;
            chmin(v->level,level) && chmin(v->c[1]->level,level);
            return move(v);
        }

        inline bool insert(Key key,Val val){
            link v = root;
            while(v->level){
                if(v->key == key){
                    v->val = val;
                    return false;
                }
                v = v->c[v->key < key];
            }

            v = assign(move(v),key,val);

            while(!v->par.expired()){
                v = v->par.lock();
                v = skew(move(v));
                v = split(move(v));
            }
            root = v;
            return true;
        }

        inline bool erase(Key key){
            link v = root;
            while(!v->level||v->key!=key){
                if(!v->level)return false;
                v = v->c[v->key < key];
            }
            while(v->level){
                const link u = v->c[!v->c[0]->level];
                v->key = u->key;
                v->val = u->val;
                v = u;
            }

            v = v->par.lock();
            v = leaf(move(v));

            while(!v->par.expired()){
                v = v->par.lock();
                v = modify(move(v));
                v = skew(move(v));
                link r = v->c[1];
                if(r->level){
                    r = skew(move(r));
                    const link rr = r->c[1];
                    if(rr->level){
                        skew(move(rr));
                    }
                }
                v = split(move(v));
                r = v->c[1];
                if(r->level){
                    split(move(r));
                }
            }
            root = v;
            return true;
        }
        inline Val get(Key b){
            link v = root;
            Val ret = 0;
            while(!isleaf(v)&& b >= v->lb){
                if(v->key <= b){
                    ret += v->val;
                    if(!isleaf(v->c[0]))ret += v->c[0]->sum;
                    v = v->c[1];
                }
                else{
                    v = v->c[0];
                }
            }
            return ret;
        }
        tree(){
            root = leaf();
        }
    };
}

typedef LL A;
typedef AA::tree B;
struct node{
    B b;
};
A mg(node& v,int key){
    return v.b.get(key);
}
A mg(A l,A r){
    return l+r;
}
namespace ST{
    node mem[1][512345];
    int cnt=0;
    node* get(){return mem[cnt++];}
}
struct seg_tree{
    int size;
    node *seg;
    void init(int l,int r,int k=0){
        auto &v=seg[k];
        v.b=B();
        if(r-l==1){
            //�t�̎��̏���
        }
        else if(r-l>1){
            int m=(l+r)/2;
            init(l,m,k*2+1);init(m,r,k*2+2);
        }
    }
    seg_tree(int n){
        size=1;
        while(size<n)size*=2;
        seg=ST::get();
        init(0,size);
    }
#define LQ a,b,k*2+1,l,m
#define RQ a,b,k*2+2,m,r
    A get(int a,int b,int k,int l,int r){
        if(a<=l){
            return mg(seg[k],b-1);
        }
        int m=(l+r)/2;
        bool ll=!(m<=a);
        bool rr=!(r<=a);
        A ret;
        if(ll&&rr)ret=mg(get(LQ),get(RQ));
        else if(ll)ret=get(LQ);
        else ret=get(RQ);
        return ret;
    }
    A get(int a,int b){return get(a,b,0,0,size);}
    void ins(int a,int b,int k,int l,int r,LL key,LL val){
        if(r<=a||b<=l)return;
        seg[k].b.insert(key,val);
        if(a<=l&&r<=b){

        }
        else{
            int m=(l+r)/2;
            ins(LQ,key,val);
            ins(RQ,key,val);
        }
    }
    void ins(int a,int b,LL val){
        ins(a,a+1,0,0,size,b-1,val);
    }
    void del(int a,int b,int k,int l,int r,LL key){
        if(r<=a||b<=l)return;
        seg[k].b.erase(key);
        if(a<=l&&r<=b){

        }
        else{
            int m=(l+r)/2;
            del(LQ,key);
            del(RQ,key);
        }
    }
    void del(int a,int b){
        del(a,a+1,0,0,size,b-1);
    }

};

int main(){
    int n,q;
    scanf("%d%d",&n,&q);
    vector<int> v(n);
    vector<set<int>> x(n);
    seg_tree seg(n+10);
    REP(i,n){
        int a;
        scanf("%d",&a);
        a--;
        v[i]=a;
        x[a].insert(i);
    }
    REP(i,n){
        int prv=-1;
        for(auto &now:x[i]){
            if(prv>=0){
                int len = now-prv;
                seg.ins(prv,now+1,len);
            }
            prv=now;
        }
    }

    REP(i,q){
        int type;
        scanf("%d",&type);
        if(type==2){
            int l,r;
            scanf("%d%d",&l,&r);
            printf("%lld\n",seg.get(l-1,r));
        }
        else{
            int p,val;
            scanf("%d%d",&p,&val);
            p--;
            val--;
            if(v[p]==val)continue;
            {//erase
                auto& td=x[v[p]];
                {
                    auto ym=td.find(p);
                    if(ym!=td.begin()){
                        auto yb=ym;yb--;
                        seg.del(*yb,(*ym)+1);
                    }
                    {
                        auto ye=ym;ye++;
                        if(ye!=td.end()){
                            seg.del(*ym,(*ye)+1);

                            if(ym!=td.begin()){
                                auto yb=ym;yb--;
                                seg.ins(*yb,(*ye)+1,(*ye-(*yb)));

                            }
                        }
                    }
                }
                td.erase(p);
            }

            v[p]=val;
            {//insert
                auto& td=x[val];
                td.insert(p);
                {
                    auto ym=td.find(p);
                    if(ym!=td.begin()){
                        auto yb=ym;yb--;
                        seg.ins(*yb,(*ym)+1,*ym-*yb);
                    }
                    {
                        auto ye=ym;ye++;
                        if(ye!=td.end()){

                            if(ym!=td.begin()){
                                auto yb=ym;yb--;
                                seg.del(*yb,(*ye)+1);
                            }

                            seg.ins(*ym,(*ye)+1,*ye-*ym);
                        }
                    }
                }
            }
        }
    }
    return 0;
}


#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

#ifdef BTK
#define DEBUG if(1)
#else
#define DEBUG if(0)
#define CIN_ONLY if(0)
struct cww{cww(){
    CIN_ONLY{
        ios::sync_with_stdio(false);cin.tie(0);
    }
}}star;

#endif

#define fin "\n"
#define FOR(i,bg,ed) for(int i=(bg);i<(ed);i++)
#define REP(i,n) FOR(i,0,n)
#define ALL(v) (v).begin(),(v).end()
#define fi first
#define se second
#define pb push_back
#define REC(ret, ...) std::function<ret (__VA_ARGS__)>
template <typename T>inline bool chmin(T &l,T r)
{bool a=l>r;if(a)l=r;return a;}
template <typename T>inline bool chmax(T &l,T r)
{bool a=l<r;if(a)l=r;return a;}
template <typename T>
istream& operator>>(istream &is,vector<T> &v){
    for(auto &it:v)is>>it;
    return is;
}

namespace AA{
    using Key = int;
    using Val = LL;
    constexpr int preffered_size = 4123456;
    struct node{
        int level,size;
        node* par;
        node* c[2];
        Key key,lb,ub;
        Val val,sum;
    };
    using link = node*;
    inline void update(const link v){
            //key
            v->size = v->c[0]->size + v->c[1]->size + !!v->level;
            v->lb   = v->c[0]->level ? v->c[0]->lb : v->key;
            v->ub   = v->c[1]->level ? v->c[1]->ub : v->key;
            //val
            v->sum = v->val;
            v->sum += v->c[0]->level ? v->c[0]->sum : 0;
            v->sum += v->c[1]->level ? v->c[1]->sum : 0;
        }

    //-memory pool--------------------------
    namespace pool{
        node mem[preffered_size];
        struct stack{
            node* stk[preffered_size];
            int size;
            stack(){
                size = 0;
                REP(i,preffered_size)
                    stk[i]=(mem+i);
            }
            inline node* const pop(){
                return stk[size++];
            }
            inline void push(node* const p){
                stk[--size]=p;
            }
        }stk;
        inline link get(){
            link ret = stk.pop();
            ret->par = NULL;
            return ret;
        }
        inline void trash(link v){
            stk.push(v);
        }

    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    inline const link leaf(const link lf = pool::get()){
        lf->level = 0;
        lf->size = 0;
        lf->c[0] = NULL;
        lf->c[1] = NULL;
        return lf;
    }
    inline bool isleaf(const link x){
        return !x->level;
    }
    inline const link assign(const link v,Key key,Val val){
        v->level = 1;
        v->key   = v->lb = v->ub = key;
        v->val   = val;
        v->c[0]  = leaf();v->c[0]->par = v;
        v->c[1]  = leaf();v->c[1]->par = v;
        update(v);
        return v;
    }

    struct tree{
        link root;
        inline const link skew(const link v){
            const link l = v->c[0];
            if(v->level == l->level){
                v->c[0] = l->c[1];
                l->c[1] = v;
                v->c[0]->par = v;
                l->par = v->par;
                v->par = l;
                if(!!l->par){
                    const link p = l->par;
                    p->c[p->key < l->key] = l;
                }
                update(v);
                update(l);
                return l;
            }
            else{
                update(v);
                return v;
            }
        }
        inline const link split(const link v){
            const link r  = v->c[1];
            const link rr = r->c[1];
            if(v->level == r->level
               && v->level == rr->level){
                v->c[1] = r->c[0];
                r->c[0] = v;
                v->c[1]->par = v;
                r->par = v->par;
                v->par = r;
                r->level ++;
                if(!!r->par){
                    const link p = r->par;
                    p->c[p->key < r->key] = r;
                }
                update(v);
                update(r);
                return r;
            }
            else{
                update(v);
                return v;
            }
        }

        inline const link modify(const link v){
            int level = min(v->c[0]->level,v->c[1]->level) + 1;
            chmin(v->level,level) && chmin(v->c[1]->level,level);
            return v;
        }

        inline bool insert(Key key,Val val){
            link v = root;
            while(v->level){
                if(v->key == key){
                    v->val = val;
                    return false;
                }
                v = v->c[v->key < key];
            }

            v = assign(v,key,val);

            while(!!v->par){
                v = v->par;
                v = skew(v);
                v = split(v);
            }
            root = v;
            return true;
        }
        inline link nearest(const link v){
            int dir = !!v->c[1]->level;
            if(!v->c[dir]->level)return NULL;
            link u = v->c[dir];
            dir = 1-dir;
            while(!!u->c[dir]->level){
                u = u->c[dir];
            }
            return u;
        }
        inline bool erase(Key key){
            link v = root;
            while(!v->level||v->key!=key){
                if(!v->level)return false;
                v = v->c[v->key < key];
            }

            while(true){
                const link u = nearest(v);
                if(u==NULL)break;
                v->key = u->key;
                v->val = u->val;
                v = u;
            }
            pool::trash(v->c[0]);
            pool::trash(v->c[1]);

            v = leaf(v);

            while(!!v->par){
                v = v->par;
                v = modify(v);
                v = skew(v);
                link r = v->c[1];
                if(r->level){
                    r = skew(r);
                    const link rr = r->c[1];
                    if(rr->level){
                        skew(rr);
                    }
                }
                v = split(v);
                r = v->c[1];
                if(r->level){
                    split(r);
                }
            }
            root = v;
            return true;
        }

        inline Val get(Key b){
            link v = root;
            Val ret = 0;
            while(!isleaf(v)&& b >= v->lb){
                if(v->key <= b){
                    ret += v->val;
                    if(!isleaf(v->c[0]))ret += v->c[0]->sum;
                    v = v->c[1];
                }
                else{
                    v = v->c[0];
                }
            }
            return ret;
        }

        tree(bool init = false){
            if(init){
                root = leaf();
            }
        }

        int tour(link v,bool print){
            int ret=v->level;
            if(!v->level)return 0;
            chmax(ret,tour(v->c[0],print));
            if(print)printf("%d[%d](%d,%d),",v->key,v->level,!!v->c[0]->level?v->c[0]->key:-1,!!v->c[1]->level?v->c[1]->key:-1);

            assert(v->level-v->c[0]->level==1);
            assert(v->level-v->c[1]->level<=1);
            assert(v->level-v->c[1]->level>=0);
            assert(v->c[0]->par==v);
            assert(v->c[1]->par==v);
            if(!!v->c[1]->level)assert(v->level-v->c[1]->c[1]->level>0);
            if(!!v->c[0]->level)assert(v->c[0]->key< v->key);
            if(!!v->c[1]->level)assert(v->c[1]->key> v->key);
            chmax(ret,tour(v->c[1],print));
            return ret;
        }
        int tour(bool print=false){
            int ret=tour(root,print);
            if(print)cout<<endl;
            return 0;
        }
    };
}

typedef LL A;
typedef AA::tree B;
struct node{
    B b;
};
A mg(node& v,int key){
    return v.b.get(key);
}
A mg(A l,A r){
    return l+r;
}
namespace ST{
    node mem[1][512345];
    int cnt=0;
    node* get(){return mem[cnt++];}
}
struct seg_tree{
    int size;
    node *seg;
    void init(int l,int r,int k=0){
        auto &v=seg[k];
        v.b=B(true);
        if(r-l==1){
            //�t�̎��̏���
        }
        else if(r-l>1){
            int m=(l+r)/2;
            init(l,m,k*2+1);init(m,r,k*2+2);
        }
    }
    seg_tree(int n){
        size=1;
        while(size<n)size*=2;
        seg=ST::get();
        init(0,size);
    }
#define LQ a,b,k*2+1,l,m
#define RQ a,b,k*2+2,m,r
    A get(int a,int b,int k,int l,int r){
        if(a<=l){
            return mg(seg[k],b-1);
        }
        int m=(l+r)/2;
        bool ll=!(m<=a);
        bool rr=!(r<=a);
        A ret;
        if(ll&&rr)ret=mg(get(LQ),get(RQ));
        else if(ll)ret=get(LQ);
        else ret=get(RQ);
        return ret;
    }
    A get(int a,int b){return get(a,b,0,0,size);}
    void ins(int a,int b,int k,int l,int r,LL key,LL val){
        if(r<=a||b<=l)return;
        //cout<<"ins:"<<k<<" "<<key<<endl;
        seg[k].b.insert(key,val);
        //seg[k].b.tour(k==0);
        if(a<=l&&r<=b){

        }
        else{
            int m=(l+r)/2;
            ins(LQ,key,val);
            ins(RQ,key,val);
        }
    }
    void ins(int a,int b,LL val){
        ins(a,a+1,0,0,size,b-1,val);
    }
    void del(int a,int b,int k,int l,int r,LL key){
        if(r<=a||b<=l)return;
        //cout<<"del:"<<k<<" "<<key<<endl;
        seg[k].b.erase(key);
        //seg[k].b.tour(k==0);

        if(a<=l&&r<=b){

        }
        else{
            int m=(l+r)/2;
            del(LQ,key);
            del(RQ,key);
        }
    }
    void del(int a,int b){
        del(a,a+1,0,0,size,b-1);
    }

};

int main(){
    int n,q;
    scanf("%d%d",&n,&q);
    vector<int> v(n);
    vector<set<int>> x(n);
    seg_tree seg(n+10);
    REP(i,n){
        int a;
        scanf("%d",&a);
        a--;
        v[i]=a;
        x[a].insert(i);
    }
    REP(i,n){
        int prv=-1;
        for(auto &now:x[i]){
            if(prv>=0){
                int len = now-prv;
                seg.ins(prv,now+1,len);
            }
            prv=now;
        }
    }

    REP(i,q){
        int type;
        scanf("%d",&type);
        //cout<<"query:"<<i<<" "<<AA::pool::stk.size<<endl;
        //cout<<"type:"<<type<<" ";

        if(type==2){
            int l,r;
            scanf("%d%d",&l,&r);
            printf("%lld\n",seg.get(l-1,r));
        }
        else{
            int p,val;
            scanf("%d%d",&p,&val);
            p--;
            val--;
            //cout<<p<<" "<<val<<endl;
            if(v[p]==val)continue;
            {//erase
                auto& td=x[v[p]];
                {
                    auto ym=td.find(p);
                    if(ym!=td.begin()){
                        auto yb=ym;yb--;
                        //printf("q1 %d %d",*yb,(*ym)+1);cout<<endl;
                        seg.del(*yb,(*ym)+1);
                    }
                    {
                        auto ye=ym;ye++;
                        if(ye!=td.end()){
                            //cout<<"q2"<<endl;

                            seg.del(*ym,(*ye)+1);

                            if(ym!=td.begin()){
                                auto yb=ym;yb--;
                                //cout<<"q3"<<endl;
                                seg.ins(*yb,(*ye)+1,(*ye-(*yb)));

                            }
                        }
                    }
                }
                td.erase(p);
            }

            v[p]=val;
            {//insert
                auto& td=x[val];
                td.insert(p);
                {
                    auto ym=td.find(p);
                    if(ym!=td.begin()){
                        auto yb=ym;yb--;
                        seg.ins(*yb,(*ym)+1,*ym-*yb);
                    }
                    {
                        auto ye=ym;ye++;
                        if(ye!=td.end()){
                            if(ym!=td.begin()){
                                auto yb=ym;yb--;
                                seg.del(*yb,(*ye)+1);
                            }

                            seg.ins(*ym,(*ye)+1,*ye-*ym);
                        }
                    }
                }
            }
        }
    }
    return 0;
}


#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

#ifdef BTK
#define DEBUG if(1)
#else
#define DEBUG if(0)
#define CIN_ONLY if(0)
struct cww{cww(){
    CIN_ONLY{
        ios::sync_with_stdio(false);cin.tie(0);
    }
}}star;

#endif

#define fin "\n"
#define FOR(i,bg,ed) for(int i=(bg);i<(ed);i++)
#define REP(i,n) FOR(i,0,n)
#define ALL(v) (v).begin(),(v).end()
#define fi first
#define se second
#define pb push_back
#define REC(ret, ...) std::function<ret (__VA_ARGS__)>
template <typename T>inline bool chmin(T &l,T r)
{bool a=l>r;if(a)l=r;return a;}
template <typename T>inline bool chmax(T &l,T r)
{bool a=l<r;if(a)l=r;return a;}
template <typename T>
istream& operator>>(istream &is,vector<T> &v){
    for(auto &it:v)is>>it;
    return is;
}

namespace RBST{
    unsigned yy=1145141919;
    inline unsigned xorshift(){yy=yy^(yy<<13);yy=yy^(yy>>17);return yy=yy^(yy<<5);}
    constexpr int MASK  = 65535;
    inline int randInt(){return (int) (xorshift()&MASK);}

    typedef int KEY;
    const int RBST_SIZE =2123456;
    struct node{
        KEY val,lb,ub;
        LL latte,sum;
        int sz;
        node *lc,*rc;//�q�ւ̃|�C���^
        node *p;//�e�ւ̃|�C���^
    };
    typedef pair<node*,node*> pn;
    inline int size(node* v){return !v?0:v->sz;}
    inline node* update(node*v){
        if(!v)return v;
        v->sz = 1 + size(v->lc) + size(v->rc);
        v->ub  = !(v->rc) ? v->val : v->rc->ub;
        v->lb  = !(v->lc) ? v->val : v->lc->lb;
        v->sum = v->latte;
        v->sum += !(v->lc)?0:v->lc->sum;
        v->sum += !(v->rc)?0:v->rc->sum;
        if(v->rc!=NULL)v->rc->p=v;
        if(v->lc!=NULL)v->lc->p=v;
        v->p=NULL;
        return v;
    }

    namespace pool{
        node mem[RBST_SIZE],*que[RBST_SIZE];
        int bg=0;
        struct RB{
            RB(){REP(i,RBST_SIZE) que[i] = &mem[i];}
        }ST;
        void push(node* m){
            que[--bg] = m;
        }
        node* get(node val){
            node* ret = que[bg++];
            *ret = val;
            return ret;
        }
        node *get(KEY v,LL val){
            return get(node{v,v,v,val,val,1,NULL,NULL,NULL});
        }

    }
    inline int lr(LL a,LL b){return randInt()*(a+b)<a*MASK;}
    //l ��r���}�[�W���Ă�
    node* merge(node *l,node *r){
        if(!l || !r)return !l ? update(r):update(l);
        node* ret;
        if( lr(l->sz,r->sz) ){
            l->rc = merge(l->rc,r);
            ret= update(l);
        }else{
            r->lc = merge(l,r->lc);
            ret= update(r);
        }
        return ret;
    }
    //(-inf,key),[key,inf) �ɕ���
    pn split(node *v,KEY key){
        if(!v) return pn(NULL,NULL);
        if( v->val < key){
            pn s = split(v->rc,key);
            v->rc = s.fi;
            return pn(update(v),s.se);
        }
        else {
            pn s = split(v->lc,key);
            v->lc = s.se;
            return pn(s.fi,update(v));
        }
    }
    node* lower_bound(node *v,KEY key){
        if(!v)return v;
        if(v->val < key){
            return lower_bound(v->rc,key);
        }
        else {
            node *u = lower_bound(v->lc,key);
            return !u ? v : u;
        }
    }
    node* min(node *v){
        return !v->lc?v:min(v->lc);
    }
    node* max(node *v){
        return !v->rc?v:max(v->rc);
    }

    node* nxt(node *v,node *p=NULL){
        if(!p){
            if(v->rc!=NULL)return min(v->rc);
        }
        else{
            if(v->lc==p)return v;
        }
        return !v->p ? NULL :nxt(v->p,v);
    }
    node* prv(node *v,node *p=NULL){
        if(!p){
            if(v->lc!=p)return max(v->lc);
        }
        else{
            if(v->rc==p)return v;
        }
        return !v->p ? NULL :prv(v->p,v);
    }

    struct RBST{
        node* root;
        RBST(){
            root = NULL;
        }
        bool isempty(){
            return !root;
        }
        int size(){
            if(isempty())return 0;
            else root->sz;
        }
        bool exist(KEY key){
            node *v=lower_bound(root,key);
            return v && v->val == key;
        }
        bool erase(KEY key){
            auto l_mr=split(root,key);

            auto m_r=split(l_mr.se,key+1);
            root = merge(l_mr.fi,m_r.se);

            if(!m_r.fi)return false;
            else {
                pool::push(m_r.fi);

                return true;
            }
        }
        bool insert(KEY key,LL val){
            node *ins=pool::get(key,val);
            auto l_r = split(root,key);
            root = merge(merge(l_r.fi,ins),l_r.se);
            return true;
        }
        LL get(KEY a,KEY b,node*v){
            if(v==NULL)return 0;
            if(a<=v->lb&&v->ub<=b){
                return v->sum;
            }
            if(v->ub<a||b<v->lb){
                return 0;
            }
            LL ret=a<=v->val&&v->val<=b?v->latte:0;
            ret+=get(a,b,v->lc);
            ret+=get(a,b,v->rc);
            return ret;
        }
        LL get(KEY a,KEY b){
            return get(a,b,root);
        }
    };
}

typedef LL A;
typedef RBST::RBST B;
struct node{
    B b;
};
A mg(node& v,int key){
    return v.b.get(-1,key);
}
A mg(A l,A r){
    return l+r;
}
namespace ST{
    node mem[1][2123456];
    int cnt=0;
    node* get(){return mem[cnt++];}
}
struct seg_tree{
    int size;
    node *seg;
    void init(int l,int r,int k=0){
        auto &v=seg[k];
        v.b=B();
        if(r-l==1){
            //�t�̎��̏���
        }
        else if(r-l>1){
            int m=(l+r)/2;
            init(l,m,k*2+1);init(m,r,k*2+2);
        }
    }
    seg_tree(int n){
        size=1;
        while(size<n)size*=2;
        seg=ST::get();
        init(0,size);
    }
#define LQ a,b,k*2+1,l,m
#define RQ a,b,k*2+2,m,r
    A get(int a,int b,int k,int l,int r){
        if(a<=l){
            DEBUG cout<<l<<r<<" "<<seg[k].b.size()<<" "<<mg(seg[k],b-1)<<fin;
            return mg(seg[k],b-1);
        }
        int m=(l+r)/2;
        bool ll=!(m<=a);
        bool rr=!(r<=a);
        A ret;
        if(ll&&rr)ret=mg(get(LQ),get(RQ));
        else if(ll)ret=get(LQ);
        else ret=get(RQ);
        return ret;
    }
    A get(int a,int b){return get(a,b,0,0,size);}
    void ins(int a,int b,int k,int l,int r,LL key,LL val){
        if(r<=a||b<=l)return;
        seg[k].b.insert(key,val);
        if(a<=l&&r<=b){

        }
        else{
            int m=(l+r)/2;
            ins(LQ,key,val);
            ins(RQ,key,val);
        }
    }
    void ins(int a,int b,LL val){
        node q;
        ins(a,a+1,0,0,size,b-1,val);
    }
    void del(int a,int b,int k,int l,int r,LL key){
        if(r<=a||b<=l)return;
        seg[k].b.erase(key);
        if(a<=l&&r<=b){

        }
        else{
            int m=(l+r)/2;
            del(LQ,key);
            del(RQ,key);
        }
    }
    void del(int a,int b){
        node q;
        del(a,a+1,0,0,size,b-1);
    }

};

int main(){
    int n,q;
    scanf("%d%d",&n,&q);
    vector<int> v(n);
    vector<set<int>> x(n);
    seg_tree seg(n+10);
    REP(i,n){
        int a;
        scanf("%d",&a);
        a--;
        v[i]=a;
        x[a].insert(i);
    }
    REP(i,n){
        int prv=-1;
        for(auto &now:x[i]){
            if(prv>=0){
                int len = now-prv;
                seg.ins(prv,now+1,len);
            }
            prv=now;
        }
    }

    auto check=[&](){
        FOR(l,1,n+1)FOR(r,l,n+1){
            vector<set<int>> s(n);
            FOR(i,l-1,r)s[v[i]].insert(i);
            LL ans=0;

            REP(i,n)if(s[i].size()){
                ans+=(*s[i].rbegin())-(*s[i].begin());
            }

            if(ans!=seg.get(l-1,r)){
                return false;
            }
        }
        return true;
    };

    REP(i,q){
        int type;
        scanf("%d",&type);
        if(type==2){
            int l,r;
            scanf("%d%d",&l,&r);
            printf("%lld\n",seg.get(l-1,r));
        }
        else{
            int p,val;
            scanf("%d%d",&p,&val);
            p--;
            val--;
            if(v[p]==val)continue;
            {//erase
                auto& td=x[v[p]];
                {
                    auto ym=td.find(p);
                    if(ym!=td.begin()){
                        auto yb=ym;yb--;
                        seg.del(*yb,(*ym)+1);

                    }
                    {
                        auto ye=ym;ye++;
                        if(ye!=td.end()){
                            seg.del(*ym,(*ye)+1);

                            if(ym!=td.begin()){
                                auto yb=ym;yb--;
                                seg.ins(*yb,(*ye)+1,(*ye-(*yb)));

                            }
                        }
                    }
                }
                td.erase(p);
            }

            v[p]=val;
            {//insert
                auto& td=x[val];
                td.insert(p);
                {
                    auto ym=td.find(p);
                    if(ym!=td.begin()){
                        auto yb=ym;yb--;
                        seg.ins(*yb,(*ym)+1,*ym-*yb);
                    }
                    {
                        auto ye=ym;ye++;
                        if(ye!=td.end()){

                            if(ym!=td.begin()){
                                auto yb=ym;yb--;
                                seg.del(*yb,(*ye)+1);
                            }

                            seg.ins(*ym,(*ye)+1,*ye-*ym);
                        }
                    }
                }
            }
        }
    }
    return 0;
}