Description:
有一个长度为n的序列, 有一些询问l r a b,表示区间[l,r]中数权值在[a,b]中的数的种类数。
Solution:
nsqrt(n)logn的很容易想到,但是会超。
考虑莫队时如何快速计算答案?把权值分块,块内统计答案,每次询问只需sqrt(n)。
故总的时间复杂度为nsqrt(n)+msqrt(n)
Code:
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstring> 4 #include <string> 5 #include <algorithm> 6 #include <cmath> 7 8 using namespace std; 9 10 #define REP(i, a, b) for (int i = (a), i##_end_ = (b); i <= i##_end_; ++i) 11 const int maxn = 1e5+10, maxm = 1e6+10; 12 int n, m, a[maxn]; 13 int bel[maxn], l[maxn], r[maxn], s_block; 14 struct Query 15 { 16 int l, r, a, b, id; 17 void read(int i) { scanf("%d %d %d %d", &l, &r, &a, &b), id = i; } 18 bool operator < (const Query &AI) const { return bel[l] == bel[AI.l] ? r < AI.r : l < AI.l; } 19 }q[maxm]; 20 int s[1100], c[maxn], ans[maxm]; 21 22 int calc(int x, int y) 23 { 24 int L = bel[x], R = bel[y], ret = 0; 25 if (L == R) 26 { 27 REP(i, x, y) if (c[i]) ret ++; 28 } 29 else 30 { 31 REP(i, L+1, R-1) ret += s[i]; 32 REP(i, x, r[L]) if (c[i]) ret ++; 33 REP(i, l[R], y) if (c[i]) ret ++; 34 } 35 return ret; 36 } 37 38 void add(int x) { if (++c[x] == 1) s[bel[x]] ++; } 39 40 void del(int x) { if (--c[x] == 0) s[bel[x]] --; } 41 42 void solve() 43 { 44 int now_l = 1, now_r = 0; 45 REP(i, 1, m) 46 { 47 for (; now_r < q[i].r; add(a[++now_r])); 48 for (; now_l > q[i].l; add(a[--now_l])); 49 for (; now_r > q[i].r; del(a[now_r--])); 50 for (; now_l < q[i].l; del(a[now_l++])); 51 ans[q[i].id] = calc(q[i].a, q[i].b); 52 } 53 } 54 55 int main() 56 { 57 scanf("%d %d", &n, &m); 58 REP(i, 1, n) scanf("%d", &a[i]); 59 int block = int(sqrt(n)); 60 REP(i, 1, n) 61 { 62 bel[i] = i/block+1, r[bel[i]] = i; 63 if (i == 1 || bel[i] != bel[i-1]) l[bel[i]] = i; 64 } 65 s_block = n/block+1; 66 REP(i, 1, m) q[i].read(i); 67 sort(q+1, q+m+1); 68 solve(); 69 REP(i, 1, m) printf("%d ", ans[i]); 70 return 0; 71 }
//if为了省行,少了括号,调了很久