bzoj5016 [SNOI2017]一个简单的询问

bzoj5016 [SNOI2017]一个简单的询问

给你一个长度为 (N) 的序列 (a_i)(1leq ileq N) ,和 (Q) 组询问,每组询问读入 (l_1, r_1, l_2, r_2) ,需输出

[sumlimits_{x=0}^infty ext{get}(l_1, r_1, x)cdot ext{get}(l_2, r_2, x) ]

( ext{get}(l, r, x)) 表示计算区间 ([l, r]) 中,数字 (x) 出现了多少次。

(1leq N, Qleq5 imes10^4, a_iin[1, N])

莫队


如果直接暴力莫队,时间复杂度是假的(

首先,如果 (l_1=l_2=1) ,是可以直接用莫队 (O(sqrt n)) 求出的

现在把询问拆成 ([1, x][1, y]) 的形式

(A_1=[l_1, r_1], A_2=[l_2, r_2], B_1=[1, l_1-1], B_2=[1, l_2-1])

先看看 ((A_1+B_1)(A_2+B_2)) 是什么

(A_1A_2+A_1B_2+A_2B_1+B_1B_2)

(A_1A_2, B_1B_2) 是可以直接求的

再把 (A_1B_2) 拆成 ((A_1+B_1)B_2-B_1B_2) ,把 (A_2B_1) 拆成 ((A_2+B_2)B_1-B_1B_2)

( herefore) $$(A_1+B_1)(A_2+B_2)=A_1A_2+(A_1+B_1)B_2+(A_2+B_2)B_1-B_1B_2$$

( herefore) $$A_1A_2=(A_1+B_1)(A_2+B_2)-(A_1+B_1)B_2-(A_2+B_2)B_1+B_1B_2$$

容易发现,上式右侧每一项都是形如 ([1, x]) 的,然后就上莫队辣

时间复杂度 (O(sqrt n))

代码

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

typedef long long ll;
const int maxn = 5e4 + 10;
ll ans[maxn];
int n, m, tot, bsz, a[maxn], bl[maxn], c1[maxn], c2[maxn];

struct Query {
  int l, r, op, tid;
  Query(int _l = 0, int _r = 0, int _o = 0, int _t = 0) :
    l(_l), r(_r), op(_o), tid(_t) {}
  bool operator < (const Query& o) const {
    return bl[l] == bl[o.l] ? r > o.r : l < o.l;
  }
} Q[maxn << 2];

int main() {
  scanf("%d", &n);
  bsz = sqrt(n);
  for (int i = 1; i <= n; i++) {
    scanf("%d", a + i);
    bl[i] = (i - 1) / bsz + 1;
  }
  scanf("%d", &m);
  for (int i = 1; i <= m; i++) {
    int l1, l2, r1, r2;
    scanf("%d %d %d %d", &l1, &r1, &l2, &r2);
    Q[++tot] = Query(r1, r2, 1, i);
    if (l1 > 1) Q[++tot] = Query(l1 - 1, r2, -1, i);
    if (l2 > 1) Q[++tot] = Query(r1, l2 - 1, -1, i);
    if (l1 > 1 && l2 > 1) Q[++tot] = Query(l1 - 1, l2 - 1, 1, i);
  }
  sort(Q + 1, Q + tot + 1);
  int l = 0, r = 0; ll now = 0;
  for (int i = 1; i <= tot; i++) {
    while (l < Q[i].l) {
      l++, c1[a[l]]++, now += c2[a[l]];
    }
    while (l > Q[i].l) {
      c1[a[l]]--, now -= c2[a[l]], l--;
    }
    while (r < Q[i].r) {
      r++, c2[a[r]]++, now += c1[a[r]];
    }
    while (r > Q[i].r) {
      c2[a[r]]--, now -= c1[a[r]], r--;
    }
    ans[Q[i].tid] += Q[i].op * now;
  }
  for (int i = 1; i <= m; i++) {
    printf("%lld
", ans[i]);
  }
  return 0;
}
原文地址:https://www.cnblogs.com/Juanzhang/p/10605613.html