[luoguP1972] [SDOI2009]HH的项链(莫队 || 树状数组 || 主席树)

传送门

 

莫队基础题,适合我这种初学者。

莫队是离线算法,通常不带修改,时间复杂度为 O(n√n)

我们要先保证通过 [ l , r ] 求得 [ l , r + 1 ] , [ l , r - 1 ] , [ l - 1 , r ] , [ l + 1 , r ] 的效率是O(1)

对于莫队的理解,移步远航休息栈

 

——代码

 1 #include <cmath>
 2 #include <cstdio>
 3 #include <iostream>
 4 #include <algorithm>
 5 
 6 int n, m, S, ans = 1;
 7 int a[50001], ton[1000001], anslist[200001];
 8 struct node
 9 {
10     int l, r, id, num;
11 }q[200001];
12 
13 inline int read()
14 {
15     int x = 0;
16     char ch = getchar();
17     for(; !isdigit(ch); ch = getchar());
18     for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
19     return x;
20 }
21 
22 inline bool cmp(node x, node y)
23 {
24     return x.id ^ y.id ? x.id < y.id : x.r < y.r;
25 }
26 
27 int main()
28 {
29     int i, x, y;
30     n = read();
31     for(i = 1; i <= n; i++) a[i] = read();
32     m = read();
33     for(i = 1; i <= m; i++) q[i].l = read(), q[i].r = read(), q[i].num = i;
34     S = sqrt(n);
35     for(i = 1; i <= m; i++) q[i].id = q[i].l / S + 1;
36     std::sort(q + 1, q + m + 1, cmp);
37     x = q[1].l, y = q[1].l;
38     ton[a[x]]++;
39     for(i = 1; i <= m; i++)
40     {
41         while(x < q[i].l)
42         {
43             ton[a[x]]--;
44             if(!ton[a[x]]) ans--;
45             x++;
46         }
47         while(x > q[i].l)
48         {
49             x--;
50             if(!ton[a[x]]) ans++;
51             ton[a[x]]++;
52         }
53         while(y > q[i].r)
54         {
55             ton[a[y]]--;
56             if(!ton[a[y]]) ans--;
57             y--;
58         }
59         while(y < q[i].r)
60         {
61             y++;
62             if(!ton[a[y]]) ans++;
63             ton[a[y]]++;
64         }
65         anslist[q[i].num] = ans;
66     }
67     for(i = 1; i <= m; i++) printf("%d\n", anslist[i]);
68     return 0;
69 }
View Code

代码量真是友善啊

 

还可以用离线用树状数组来做.

以下是hzwer的解法:

这题首先在线是没法做的,所以我们可以考虑离线算法

首先记录下每种颜色的下一种颜色所在的位置

将所有询问按照左端点进行排序

将所有颜色的第一个点x a[x]++

然后从左往右扫

扫到一个点x将a[next[x]]++

碰到一个询问l,r输出sum[r]-sum[l-1]

其中sum是a数组的前缀和

求前缀和可以用树状数组

 

——代码

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 
 6 const int MAXN = 50001;
 7 int n, m, max;
 8 int head[MAXN], next[MAXN], a[MAXN], ans[MAXN], c[1000001];
 9 struct node
10 {
11     int l, r, id;
12 }q[MAXN];
13 
14 inline int read()
15 {
16     int x = 0, f = 1;
17     char ch = getchar();
18     for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
19     for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
20     return x * f;
21 }
22 
23 inline int Max(int x, int y)
24 {
25     return x > y ? x : y;
26 }
27 
28 inline void update(int x)
29 {
30     for(; x <= n; x += x & -x) c[x]++;
31 }
32 
33 inline int query(int x)
34 {
35     int ret = 0;
36     for(; x; x -= x & -x) ret += c[x];
37     return ret;
38 }
39 
40 inline bool cmp(node x, node y)
41 {
42     return x.l < y.l;
43 }
44 
45 int main()
46 {
47     int i, j;
48     n = read();
49     memset(head, -1, sizeof(head));
50     for(i = 1; i <= n; i++) a[i] = read(), max = Max(max, a[i]);
51     for(i = n; i; i--) next[i] = head[a[i]], head[a[i]] = i;
52     for(i = 1; i <= max; i++)
53         if(head[i] ^ -1)
54             update(head[i]);
55     m = read();
56     for(i = 1; i <= m; i++)
57     {
58         q[i].l = read();
59         q[i].r = read();
60         q[i].id = i;
61     }
62     std::sort(q + 1, q + m + 1, cmp);
63     j = 1;
64     for(i = 1; i <= n; i++)
65     {
66         while(q[j].l == i) ans[q[j].id] = query(q[j].r) - query(q[j].l - 1), j++;
67         if(next[i] ^ -1) update(next[i]);
68     }
69     for(i = 1; i <= m; i++) printf("%d\n", ans[i]);
70     return 0;
71 }
View Code

 

 

主席树也是个好方法。

保存每个点的上一个和它颜色相同的点的位置(如果没有就是0)

然后以每个点的前驱为下标建主席数。

统计时只需要统计当前区间 [x,y] 中前驱小于 x 的点的个数

 

——代码

 1 #include <cstdio>
 2 #include <iostream>
 3 
 4 const int MAXN = 50001;
 5 int n, m, cnt;
 6 int a[MAXN], head[1000001], pre[MAXN], root[MAXN], sum[MAXN * 20], ls[MAXN * 20], rs[MAXN * 20];
 7 
 8 inline int read()
 9 {
10     int x = 0, f = 1;
11     char ch = getchar();
12     for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
13     for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
14     return x * f;
15 }
16 
17 inline void update(int &now, int l, int r, int x)
18 {
19     ++cnt;
20     sum[cnt] = sum[now] + 1;
21     ls[cnt] = ls[now];
22     rs[cnt] = rs[now];
23     now = cnt;
24     if(l == r) return;
25     int mid = (l + r) >> 1;
26     if(x <= mid) update(ls[now], l, mid, x);
27     else update(rs[now], mid + 1, r, x);
28 }
29 
30 inline int query(int x, int y, int l, int r, int k)
31 {
32     if(l == r) return sum[y] - sum[x];
33     int mid = (l + r) >> 1;
34     if(k <= mid) return query(ls[x], ls[y], l, mid, k);
35     else return query(rs[x], rs[y], mid + 1, r, k) + sum[ls[y]] - sum[ls[x]];
36 }
37 
38 int main()
39 {
40     int i, x, y;
41     n = read();
42     for(i = 1; i <= n; i++)
43     {
44         a[i] = read();
45         pre[i] = head[a[i]];
46         head[a[i]] = i;
47     }
48     for(i = 1; i <= n; i++)
49     {
50         root[i] = root[i - 1];
51         update(root[i], 0, n, pre[i]);
52     }
53     m = read();
54     for(i = 1; i <= m; i++)
55     {
56         x = read();
57         y = read();
58         printf("%d\n", query(root[x - 1], root[y], 0, n, x - 1));
59     }
60     return 0;
61 }
View Code

 

posted @ 2017-05-19 19:36  zht467  阅读(174)  评论(0编辑  收藏  举报