C. Goodbye Souvenir(CDQ 或 树套树)

C. Goodbye Souvenir

∑ i = L R i − p r e A i [ p r e A i ≥ L ] \sum\limits_{i = L} ^{R} i - pre_{A_i} [pre_{A_i} \geq L] i=LRipreAi[preAiL],进一步考虑即 ∑ i − p r e A i [ i ≤ R , p r e A i ≥ L ] \sum i - pre_{A_i}[i \leq R, pre_{A_i} \geq L] ipreAi[iR,preAiL],

i ≤ R i \leq R iR

p r e A i ≥ L pre_{A_i} \geq L preAiL

③ 时间 t t t维度

由此这个问题被转化为了一个标准的三维偏序问题,第一维为时间 t t t,第二维为 i i i,第三维为 p r e A i pre_{A_i} preAi,在第三维中动态插入 i − p r e A i i - pre_{A_i} ipreAi的值。

因此考虑如下一个结构体:

struct {
    int op, x, y, f, id;
};

依次为:存放操作种类,存放 i i i,存放 p r e A i pre_{A_i} preAi,存放 i − p r e A i i - pre_{A_i} ipreAi,存放对第几个询问贡献答案。

考虑修改操作,对应 p r e [ p ] , p , n e x t [ p ] pre[p], p, next[p] pre[p],p,next[p],我们修改的是 p p p节点的信息,也就是这个节点不复存在了,

所以显然加入操作 { 1 , p , p r e [ p ] , p r e [ p ] − p , 0 } \{1, p, pre[p], pre[p] - p, 0\} {1,p,pre[p],pre[p]p,0},也就是减去之前存在的 { 1 , p , p r e [ p ] , p − p r e [ p ] , 0 } \{1, p, pre[p], p - pre[p], 0\} {1,p,pre[p],ppre[p],0}

考虑其对后继的影响,显然也要减去之前存在的后继信息,也就是加入 { 1 , n e x t [ p ] , p , p − n e x t [ p ] , 0 } \{1, next[p], p, p - next[p], 0\} {1,next[p],p,pnext[p],0}

然后加上修改后其后继的值 { 1 , n e x t [ p ] , p r e [ p ] , n e x t [ p ] − p r e [ p ] , 0 } \{1, next[p], pre[p], next[p] - pre[p], 0\} {1,next[p],pre[p],next[p]pre[p],0}

同样的对于修改后,也就是插入了一个新的值,我们也要先删去当前加入的点的后继,然后再加入当前点,以及后继的最新信息。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 1e5 + 10;

int a[N], pre[N], n, m, top;

ll ans[N], tree[N];

set<int> st[N];

struct Res {
  int op, x, y, f, id;
}Q[7 * N];

bool cmpx(Res a, Res b) {
  return a.x < b.x;
}

bool cmpid(Res a, Res b) {
  return a.id < b.id;
}

inline int lowbit(int x) {
  return x & (-x);
}

void clear(int x) {
  while (x) {
    tree[x] = 0;
    x -= lowbit(x);
  }
}

void update(int x, int v) {
  while (x) {
    tree[x] += v;
    x -= lowbit(x);
  }
}

ll query(int x) {
  ll res = 0;
  while (x <= n) {
    res += tree[x];
    x += lowbit(x);
  }
  return res;
}

void CDQ(int l, int r) {
  if (l == r) {
    return ;
  }
  int mid = l + r >> 1;
  CDQ(l, mid), CDQ(mid + 1, r);
  for (int i = l, j = mid + 1; j <= r; j++) {
    while (i <= mid && Q[i].x <= Q[j].x) {
      if (Q[i].op & 1) {
        update(Q[i].y, Q[i].f);
      }
      i++;
    }
    if (Q[j].op == 2) {
      ans[Q[j].id] += query(Q[j].y);
    }
  }
  for (int i = l; i <= mid; i++) {
    clear(Q[i].y);
  }
  sort(Q + l, Q + r + 1, cmpx);
}

int main() {
  // freopen("in.txt", "r", stdin);
  // freopen("out.txt", "w", stdout);
  // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  scanf("%d %d", &n, &m);
  for (int i = 1; i <= n; i++) {
    st[i].insert(0);
  }
  for (int i = 1; i <= n; i++) {
    scanf("%d", &a[i]);
    pre[i] = *--st[a[i]].end();
    st[a[i]].insert(i);
    Q[++top] = {1, i, pre[i], i - pre[i], 0};
  }
  for (int i = 1, op, x, y; i <= m; i++) {
    scanf("%d %d %d", &op, &x, &y);
    if (op & 1) {
      auto it = st[a[x]].lower_bound(x);
      Q[++top] = {1, x, pre[x], pre[x] - x, 0};
      it++;
      if (it != st[a[x]].end()) {
        Q[++top] = {1, *it, pre[*it], pre[*it] - *it, 0};
        pre[*it] = pre[x];
        Q[++top] = {1, *it, pre[*it], *it - pre[*it], 0};
      }
      st[a[x]].erase(x);
      a[x] = y;
      st[a[x]].insert(x);
      it = st[a[x]].lower_bound(x);
      it--;
      pre[x] = *it;
      Q[++top] = {1, x, pre[x], x - pre[x], 0};
      ++it, ++it;
      if (it != st[a[x]].end()) {
        Q[++top] = {1, *it, pre[*it], pre[*it] - *it, 0};
        pre[*it] = x;
        Q[++top] = {1, *it, pre[*it], *it - pre[*it], 0};
      }
    }
    else {
      Q[++top] = {op, y, x, 0, i};
    }
  }
  CDQ(1, top);
  sort(Q + 1, Q + 1 + top, cmpid);
  for (int i = 1; i <= top; i++) {
    if (Q[i].op == 2) {
      printf("%lld\n", ans[Q[i].id]);
    }
  }
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值