[AHOI2013]作业 & Gty的二逼妹子序列 莫队
题解:
题目要求统计一个区间内数值在[a, b]内的数的个数和种数,而这个是可以用树状数组统计出来的,所以可以考虑莫队。
考虑区间[l, r]转移到[l, r + 1],那么对于维护个数的树状数组就直接加即可。
对于维护种数的树状数组,我们额外维护一个数组num,表示数a在区间内出现了多少次,如果是新出现的,那么就加入树状数组。
如果要删除一个数并且这个数在区间内只出现了一次,那么就删除这个数。注意不论什么情况都要实时维护num数组。
然后莫队即可。
Gty的二逼妹子序列是洛谷P4867和作业的某一问是一模一样的,把数组开大点就可以过。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define AC 100100 5 6 int n, m, cnt, block; 7 int ans1[AC], ans2[AC], s[AC], num[AC], tot[AC]; 8 9 struct node{ 10 int l, r, a, b, id; 11 }q[AC]; 12 13 inline int lowbit(int x) 14 { 15 return x & (-x); 16 } 17 18 struct kkk{ 19 int a[AC]; 20 21 void add(int x, int y) 22 { 23 for(R i = x; i <= cnt; i += lowbit(i)) a[i] += y; 24 } 25 26 int find(int x) 27 { 28 int rnt = 0; 29 for(R i = x; i; i -= lowbit(i)) rnt += a[i]; 30 return rnt; 31 } 32 }c1, c2; 33 34 inline int read() 35 { 36 int x = 0;char c = getchar(); 37 while(c > '9' || c < '0') c = getchar(); 38 while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); 39 return x; 40 } 41 42 int half(int x)//查询离散化后的值 43 { 44 int l = 1, r = cnt, mid; 45 while(l < r) 46 { 47 mid = (l + r) >> 1; 48 if(num[mid] == x) return mid; 49 else if(num[mid] > x) r = mid; 50 else l = mid + 1; 51 } 52 return l; 53 } 54 55 int half1(int x)//查询最小的大于等于a的值 56 { 57 int l = 1, r = cnt, mid; 58 if(num[cnt] < x) return cnt + 1; 59 while(l < r) 60 { 61 mid = (l + r) >> 1; 62 if(num[mid] >= x) r = mid; 63 else l = mid + 1; 64 } 65 return l; 66 } 67 68 int half2(int x)//查询最大的小于等于b的值 69 { 70 int l = 1, r = cnt, mid; 71 if(num[1] > x) return 0; 72 while(l < r) 73 { 74 mid = (l + r + 1) >> 1;//强制偏右 75 if(num[mid] > x) r = mid - 1; 76 else l = mid; 77 } 78 return l; 79 } 80 81 inline bool cmp(node a, node b) 82 { 83 if(a.l / block != b.l / block) return a.l < b.l; 84 else return a.r < b.r;//分块排序 85 } 86 87 void pre() 88 { 89 n = read(), m = read(), block = sqrt(n); 90 for(R i = 1; i <= n; i ++) s[i] = num[i] = read(); 91 sort(num + 1, num + n + 1); 92 for(R i = 1; i <= n; i ++) 93 if(num[i] != num[i + 1]) num[++cnt] = num[i]; 94 for(R i = 1; i <= n; i ++) s[i] = half(s[i]);//在这里离散化,这样后面就不用调用了 95 for(R i = 1; i <= m; i ++) 96 { 97 q[i].l = read(), q[i].r = read(), q[i].id = i; 98 q[i].a = half1(read()), q[i].b = half2(read()); 99 } 100 sort(q + 1, q + m + 1, cmp); 101 } 102 103 void add(int x) 104 { 105 c1.add(x, 1); 106 if(!tot[x]) c2.add(x, 1); 107 ++ tot[x]; 108 } 109 110 void del(int x) 111 { 112 c1.add(x, -1); 113 -- tot[x]; 114 if(!tot[x]) c2.add(x, -1); 115 } 116 117 void work()//这里每个点的贡献与区间无关,相对独立,所以不用考虑顺序问题 118 { 119 int l, r; 120 for(R i = q[1].l; i <= q[1].r; i ++) add(s[i]); 121 ans1[q[1].id] = c1.find(q[1].b) - c1.find(q[1].a - 1); 122 ans2[q[1].id] = c2.find(q[1].b) - c2.find(q[1].a - 1); 123 l = q[1].l, r = q[1].r; 124 for(R i = 2; i <= m; i ++) 125 { 126 int ll = q[i].l, rr = q[i].r; 127 while(ll < l) -- l, add(s[l]); 128 while(ll > l) del(s[l]), ++ l; 129 while(rr > r) ++ r, add(s[r]); 130 while(rr < r) del(s[r]), -- r; 131 ans1[q[i].id] = c1.find(q[i].b) - c1.find(q[i].a - 1); 132 ans2[q[i].id] = c2.find(q[i].b) - c2.find(q[i].a - 1); 133 } 134 for(R i = 1; i <= m; i ++) printf("%d %d\n", ans1[i], ans2[i]); 135 } 136 137 int main() 138 { 139 // freopen("in.in", "r", stdin); 140 pre(); 141 work(); 142 // fclose(stdin); 143 return 0; 144 }