牛客暑假多校第一场 J Different Integers

题意:给你一个数组, q次询问, 每次询问都会有1个[l, r] 求 区间[1,l] 和 [r, n] 中 数字的种类是多少。

解法1, 莫队暴力:

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout);
 4 #define LL long long
 5 #define ULL unsigned LL
 6 #define fi first
 7 #define se second
 8 #define pb push_back
 9 #define lson l,m,rt<<1
10 #define rson m+1,r,rt<<1|1
11 #define max3(a,b,c) max(a,max(b,c))
12 #define min3(a,b,c) min(a,min(b,c))
13 typedef pair<int,int> pll;
14 const int inf = 0x3f3f3f3f;
15 const LL INF = 0x3f3f3f3f3f3f3f3f;
16 const LL mod =  (int)1e9+7;
17 const int N = 2e5 + 100;
18 int n, m, blo;
19 int a[N];
20 struct Node{
21     int l, r, id, ans;
22 }q[N];
23 bool cmp(Node x1, Node x2){
24     if(x1.l/blo !=  x2.l / blo) return x1.l < x2.l;
25     if((x1.l / blo) & 1)    return x1.r < x2.r;
26     else    return x1.r > x2.r;
27 }
28 int cnt[N];
29 int tot;
30 int ans[N];
31 void add(int p){
32     //cout << p << endl;
33     cnt[a[p]]++;
34     if(cnt[a[p]] == 1) tot++;
35 }
36 void Remove(int p){
37     cnt[a[p]]--;
38     if(cnt[a[p]] == 0) tot--;
39 }
40 int main(){
41     while(~scanf("%d%d", &n, &m)){
42         memset(cnt, 0, sizeof(cnt));
43         tot = 0;
44         blo = 500 + 1;
45         for(int i = 1; i <= n; i++)
46             scanf("%d", &a[i]);
47         for(int i = 1; i <= m; i++){
48             scanf("%d%d", &q[i].l, &q[i].r);
49             q[i].id = i;
50         }
51         sort(q+1, q+1+m, cmp);
52         int L = 0, R = n+1;
53         for(int i = 1; i <= m; i++){
54             int nl = q[i].l;
55             int nr = q[i].r;
56             while(L < nl) add(++L);
57             while(L > nl) Remove(L--);
58             while(R > nr) add(--R);
59             while(R < nr) Remove(R++);
60             ans[q[i].id] = tot;
61         }
62         for(int i = 1; i <= m; i++){
63             printf("%d\n", ans[i]);
64         }
65     }
66     return 0;
67 }
View Code

 

解法2, 离线询问, 按r从小到打排序, 每次r往右边移动的时候, 如果r移除的时候移除了 x 最后一次出现的位置, 那么就在x第一次出现的位置标记一下。 每次询问的时候答案就为[1,l] 标记数字出现的次数 + 右边的值。我们用树状数组来优化[1,l]的和。

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout);
 4 #define LL long long
 5 #define ULL unsigned LL
 6 #define fi first
 7 #define se second
 8 #define pb push_back
 9 #define lson l,m,rt<<1
10 #define rson m+1,r,rt<<1|1
11 #define max3(a,b,c) max(a,max(b,c))
12 #define min3(a,b,c) min(a,min(b,c))
13 typedef pair<int,int> pll;
14 const int inf = 0x3f3f3f3f;
15 const LL INF = 0x3f3f3f3f3f3f3f3f;
16 const LL mod =  (int)1e9+7;
17 const int N = 2e5 + 100;
18 int cnt[N], last[N], first[N];
19 int a[N], ans[N];
20 int n, m, tot;
21 struct Node{
22     int l, r, id;
23     bool operator <(Node &x) const{
24         return r < x.r;
25     }
26 }q[N];
27 void Add(int x){
28     while(x <= n){
29         cnt[x]++;
30         x += x&(-x);
31     }
32 }
33 int Query(int x){
34     int ret = 0;
35     while(x){
36         ret += cnt[x];
37         x -= x&(-x);
38     }
39     return ret;
40 }
41 int main(){
42     while(~scanf("%d%d", &n, &m)){
43         memset(cnt, 0, sizeof(cnt));
44         memset(last, 0, sizeof(last));
45         memset(first, 0, sizeof(first));
46         tot = 0;
47         for(int i = 1; i <= n; i++){
48             scanf("%d", &a[i]);
49             if(first[a[i]] == 0){
50                 first[a[i]] = i;
51                 tot++;
52             }
53             last[a[i]] = i;
54         }
55         for(int i = 1; i <= m; i++){
56             scanf("%d%d", &q[i].l, &q[i].r);
57             q[i].id = i;
58         }
59         sort(q+1, q+1+m);
60         int R = 1;
61         for(int i = 1; i <= n; i++){
62             while(R < q[i].r){
63                 if(last[a[R]] == R){
64                     Add(first[a[R]]);
65                     tot--;
66                 }
67                 R++;
68             }
69             ans[q[i].id] = tot + Query(q[i].l);
70         }
71         for(int i = 1; i <= m; i++)
72             printf("%d\n", ans[i]);
73     }
74     return 0;
75 }
View Code

 

还有蔡队的写法, 直接复制一份原来的数组在后面, 将2个区间的问题转化成一个区间求数字的问题,然后就可以套主席树的板子了。

posted @ 2018-07-20 14:21  Schenker  阅读(372)  评论(0编辑  收藏  举报