牛客暑假多校第一场 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 }
解法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 }
还有蔡队的写法, 直接复制一份原来的数组在后面, 将2个区间的问题转化成一个区间求数字的问题,然后就可以套主席树的板子了。