[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
", 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 }
View Code
原文地址:https://www.cnblogs.com/ww3113306/p/9614399.html