bzoj5016 [SNOI2017]一个简单的询问

bzoj5016 [SNOI2017]一个简单的询问

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

\[\sum\limits_{x=0}^\infty \text{get}(l_1,\ r_1,\ x)\cdot \text{get}(l_2,\ r_2,\ x) \]

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

\(1\leq N,\ Q\leq5\times10^4,\ a_i\in[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\)

\(\therefore\) $$(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$$

\(\therefore\) $$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\n", ans[i]);
  }
  return 0;
}
posted @ 2019-03-27 10:10  cnJuanzhang  阅读(130)  评论(0编辑  收藏  举报