带修改的莫队、树上莫队



前言

莫队大法好!只是一卡离线就GG
由于莫队都是套路,因此几乎不会做解释

  1. 莫队算法
    https://blog.sengxian.com/algorithms/mo-s-algorithm
    https://www.cnblogs.com/RabbitHu/p/MoDuiTutorial.html

数颜色 - HYSBZ 2120


思路
带修改莫队模板题

  #include <cstdio>
  #include <cstring>
  #include <algorithm>
  #include <cmath>
  using namespace std;
  const int N = 10000 + 5;
  const int M = 1e6 + 5;

  struct QQuery{
      int l, r, tim, idx;
  };
  struct QModify{
      int pos, val, pre;
  };
  int BLOCK_SIZE;
  int ans[N];
  int cnt[M];
  int a[N];
  QQuery  queq[N];
  QModify queu[N];

  bool cmp(const QQuery& a, const QQuery& b){
      if(a.l/BLOCK_SIZE != b.l/BLOCK_SIZE)         return a.l < b.l;
      else if(a.r/BLOCK_SIZE != b.r/BLOCK_SIZE)    return a.r < b.r;
      return a.tim/BLOCK_SIZE < b.tim/BLOCK_SIZE;
  }

  void moveTimeForward(int tim, int l, int r, int& ans){
      QModify& qcur = queu[tim];
      qcur.pre = a[qcur.pos];
      a[qcur.pos] = qcur.val;
      if(l <= qcur.pos && qcur.pos <= r){
          if((--cnt[qcur.pre]) == 0)  ans--;
          if((++cnt[qcur.val]) == 1)  ans++;
      }
  }

  void moveTimeBack(int tim, int l, int r, int& ans){
      QModify& qcur = queu[tim];
      a[qcur.pos] = qcur.pre;
      if(l <= qcur.pos && qcur.pos <= r){
          if((--cnt[qcur.val]) == 0)  ans--;
          if((++cnt[qcur.pre]) == 1)  ans++;
      }
  }

  void add(int pos, int& ans){
      if((++cnt[a[pos]]) == 1)    ans++;
  }

  void del(int pos, int& ans){
      if((--cnt[a[pos]]) == 0)    ans--;
  }

  int main(){
      int n, q;
      while(~scanf("%d%d", &n, &q)){
          memset(cnt, 0, sizeof(cnt));
          BLOCK_SIZE = (int)pow(n, 2.0/3);
          for(int i = 1; i <= n; i++){
              scanf("%d", &a[i]);
          }

          int ppq = 0, ppu = 0;
          while(q--){
              char s[2];
              int x, y;
              scanf("%s%d%d", s, &x, &y);
              if(s[0] == 'Q'){
                  queq[ppq] = QQuery{x, y, ppu, ppq};
                  ppq++;
              }else{
                  queu[++ppu] = QModify{x, y, 0};
              }
          }
          sort(queq, queq + ppq, cmp);

          int r = -1, l = 0, tim = 0, curans = 0;
          for(int i = 0; i < ppq; i++){
              QQuery& q = queq[i];
              while(tim < q.tim)     moveTimeForward(++tim, l, r, curans);
              while(tim > q.tim)     moveTimeBack(tim--, l, r, curans);
              while(l < q.l)         del(l++, curans);
              while(l > q.l)         add(--l, curans);
              while(r < q.r)         add(++r, curans);
              while(r > q.r)         del(r--, curans);
              ans[q.idx] = curans;
          }

          for(int i = 0; i < ppq; i++){
              printf("%d\n", ans[i]);
          }
      }
  }


Machine Learning - CodeForces - 940F


题意
给定n个整数与m个操作,操作有两种,第一种是查询[l,r]的mex{c(1),…,c(1e9)},c(x)指x出现的次数,第二种是修改一个整数的值
思路
首先需要离散化,然后套莫队
注意cnt数组和mp数组越界的问题,因为修改的数值都是新数值,因此需要双倍空间

  #include <cstdio>
  #include <cstring>
  #include <algorithm>
  #include <cmath>
  using namespace std;
  const int N = 1e5 + 5;

  struct QQuery{
      int l, r, tim, idx;
  };
  struct QModify{
      int pos, val, pre;
  };
  int BLOCK_SIZE;
  int ans[N];
  int cnt[N << 1], used[N];
  int a[N];
  int mp[N << 1], pmp;
  QQuery  queq[N];
  QModify queu[N];

  bool cmp(const QQuery& a, const QQuery& b){
      if(a.l/BLOCK_SIZE != b.l/BLOCK_SIZE)         return a.l < b.l;
      else if(a.r/BLOCK_SIZE != b.r/BLOCK_SIZE)    return a.r < b.r;
      return a.tim/BLOCK_SIZE < b.tim/BLOCK_SIZE;
  }

  void add(int val){
      used[cnt[val]]--;
      cnt[val]++;
      used[cnt[val]]++;
  }

  void del(int val){
      used[cnt[val]]--;
      cnt[val]--;
      used[cnt[val]]++;
  }

  void moveTimeForward(int tim, int l, int r){
      QModify& qcur = queu[tim];
      qcur.pre = a[qcur.pos];
      a[qcur.pos] = qcur.val;
      if(l <= qcur.pos && qcur.pos <= r){
          del(qcur.pre);
          add(qcur.val);
      }
  }

  void moveTimeBack(int tim, int l, int r){
      QModify& qcur = queu[tim];
      a[qcur.pos] = qcur.pre;
      if(l <= qcur.pos && qcur.pos <= r){
          del(qcur.val);
          add(qcur.pre);
      }
  }

  int main(){
      int n, q;
      while(~scanf("%d%d", &n, &q)){
          memset(cnt, 0, sizeof(cnt));
          memset(used, 0, sizeof(used));
          pmp = 0;
          BLOCK_SIZE = (int)pow(n, 2.0/3);

          for(int i = 1; i <= n; i++){
              scanf("%d", &a[i]);
              mp[++pmp] = a[i];
          }

          int ppq = 0, ppu = 0;
          while(q--){
              int op, x, y;
              scanf("%d%d%d", &op, &x, &y);
              if(op == 1){
                  queq[ppq] = QQuery{x, y, ppu, ppq};
                  ppq++;
              }else{
                  mp[++pmp] = y;
                  queu[++ppu] = QModify{x, y, 0};
              }
          }
          sort(mp + 1, mp + 1 + pmp);
          pmp = unique(mp + 1, mp + 1 + pmp) - mp - 1;
          for(int i = 1; i <= ppu; i++){
              queu[i].val = lower_bound(mp + 1, mp + 1 + pmp, queu[i].val) - mp;
          }
          for(int i = 1; i <= n; i++){
              a[i] = lower_bound(mp + 1, mp + 1 + pmp, a[i]) - mp;
          }
          sort(queq, queq + ppq, cmp);

          int r = 0, l = 1, tim = 0;
          for(int i = 0; i < ppq; i++){
              QQuery& q = queq[i];
              while(tim < q.tim)     moveTimeForward(++tim, l, r);
              while(tim > q.tim)     moveTimeBack(tim--, l, r);
              while(r < q.r)         add(a[++r]);
              while(r > q.r)         del(a[r--]);
              while(l < q.l)         del(a[l++]);
              while(l > q.l)         add(a[--l]);

              int mex = 1;
              while(used[mex])    mex++;
              ans[q.idx] = mex;
          }

          for(int i = 0; i < ppq; i++){
              printf("%d\n", ans[i]);
          }
      }
  }


【WC2013】糖果公园 - uoj 58


思路
树上莫队模板题

  #include <cstdio>
  #include <cstring>
  #include <cmath>
  #include <algorithm>
  using namespace std;
  const int N = 1e5 + 5;
  typedef long long ll;

  struct squery{
      int u, v, tim, idx;
  };
  struct supdate{
      int u, val, preval;
  };
  struct edge{
      int v, next;
  };
  squery  query[N];
  supdate update[N];
  int  w[N], v[N], c[N];
  edge e[N << 1];
  int  head[N], tot;
  int  belong[N];
  int  stk[N], pstk;
  int  fa[N][18];
  int  dpt[N];
  bool used[N];
  int  cnt[N];
  ll   ans[N];
  int  BLOCK_SIZE;

  bool cmp(const squery& a, const squery& b){
      if(belong[a.u] != belong[b.u])          return belong[a.u] < belong[b.u];
      else if(belong[a.v] != belong[b.v])     return belong[a.v] < belong[b.v];
      else                                    return a.tim/BLOCK_SIZE < b.tim/BLOCK_SIZE;
  }

  inline void init(){
      memset(head, -1, sizeof(head));
      memset(fa, -1, sizeof(fa));
      memset(cnt, 0, sizeof(cnt));
      memset(used, 0, sizeof(used));
      tot = 0;
  }

  inline void addEdge(int u, int v){
      e[tot] = edge{v, head[u]};
      head[u] = tot++;
  }

  void dfs(int u, int pre, int depth){
      int bottom = pstk;
      dpt[u] = depth;
      for(int i = head[u]; ~i; i = e[i].next){
          int v = e[i].v;
          if(v == pre)    continue;
          fa[v][0] = u;
          dfs(v, u, depth + 1);
          if(pstk - bottom >= BLOCK_SIZE){
              while(pstk != bottom){
                  belong[stk[pstk--]] = u;
              }
          }
      }
      stk[++pstk] = u;
  }

  void initLCA(int n){
      for(int j = 1; j <= 17; j++){
          for(int u = 1; u <= n; u++){
              if(fa[u][j - 1] == -1)  continue;
              fa[u][j] = fa[fa[u][j - 1]][j - 1];
          }
      }
  }

  void initBlockAndLCA(int n){
      BLOCK_SIZE = (int)pow(n, 2.0/3);
      pstk = 0;
      dfs(1, -1, 0);
      while(pstk >= 1){
          belong[stk[pstk--]] = 1;
      }
      initLCA(n);
  }

  int getFa(int u, int v){
      if(dpt[u] > dpt[v])     swap(u, v);
      for(int j = 17; j >= 0; j--){
          if(fa[v][j] == -1 || dpt[fa[v][j]] < dpt[u])    continue;
          v = fa[v][j];
      }
      if(u == v)  return u;
      for(int j = 17; j >= 0; j--){
          if(fa[v][j] == -1 || fa[u][j] == -1 || fa[u][j] == fa[v][j])    continue;
          u = fa[u][j], v = fa[v][j];
      }
      return fa[u][0];
  }

  void reverse(int u, ll& ans){
      if(used[u]){
          ans -= (ll)v[c[u]] * w[cnt[c[u]]];
          cnt[c[u]]--;
      }else{
          cnt[c[u]]++;
          ans += (ll)v[c[u]] * w[cnt[c[u]]];
      }
      used[u] ^= 1;
  }

  void change(int u, int val, ll& ans){
      if(used[u]){
          reverse(u, ans);
          c[u] = val;
          reverse(u, ans);
      }else{
          c[u] = val;
      }
  }

  void moveTimeForward(int tim, ll& ans){
      int u = update[tim].u;
      update[tim].preval = c[u];
      change(u, update[tim].val, ans);
  }

  void moveTimeBack(int tim, ll& ans){
      int u = update[tim].u;
      change(u, update[tim].preval, ans);
  }

  void moveNode(int u, int v, ll& ans){
      while(u != v){
          if(dpt[u] < dpt[v]){
              reverse(v, ans);
              v = fa[v][0];
          }else{
              reverse(u, ans);
              u = fa[u][0];
          }
      }
  }

  int main(){
      int n, m, q;
      while(~scanf("%d%d%d", &n, &m, &q)){
          init();
          for(int i = 1; i <= m; i++)     scanf("%d", &v[i]);
          for(int i = 1; i <= n; i++)     scanf("%d", &w[i]);
          for(int i = 1; i <= n - 1; i++){
              int u, v;
              scanf("%d%d", &u, &v);
              addEdge(u, v);
              addEdge(v, u);
          }
          for(int i = 1; i <= n; i++)     scanf("%d", &c[i]);
          initBlockAndLCA(n);

          int pp = 0, pq = 0;
          while(q--){
              int type, x, y;
              scanf("%d%d%d", &type, &x, &y);
              if(type == 0){
                  update[++pq] = supdate{x, y, -1};
              }else{
                  query[pp] = squery{x, y, pq, pp};
                  pp++;
              }
          }
          sort(query, query + pp, cmp);

          int u = query[0].u, v = query[0].v, tim = 0;
          ll curans = 0;
          while(tim < query[0].tim)   moveTimeForward(++tim, curans);
          moveNode(u, v, curans);
          reverse(getFa(u, v), curans);
          ans[query[0].idx] = curans;
          reverse(getFa(u, v), curans);
          for(int i = 1; i < pp; i++){
              while(tim < query[i].tim)       moveTimeForward(++tim, curans);
              while(tim > query[i].tim)       moveTimeBack(tim--, curans);
              int nu = query[i].u, nv = query[i].v;
              moveNode(u, nu, curans);
              moveNode(v, nv, curans);
              int lca = getFa(nu, nv);
              reverse(lca, curans);
              ans[query[i].idx] = curans;
              reverse(lca, curans);
              u = nu, v = nv;
          }
          for(int i = 0; i < pp; i++){
              printf("%lld\n", ans[i]);
          }
      }
  }