Luogu4113 [HEOI2012]采花

题目大意:给定一个长度为$n$的序列$a_i$,$m$次询问,每次询问$[l,r]$,求在区间内有多少个数出现了至少2次。

数据范围:$1\leq l\leq r\leq n\leq 2*10^6,1\leq m,a_i\leq 2*10^6$


首先我们考虑设$pre_i$表示上一个与$a_i$相等的位置。

所以$a_i$对答案有贡献当且仅当$l\leq pre_i<i\leq r$

这是一个经典的二维数点问题,cdq分治可以解决,但是。。。

所以我们换一种更优的做法,我们每次遍历到一个位置$a_i$的时候,考虑如果$l\leq pre_i<i\leq r$的时候,$i$会有贡献。

设$c_i$表示当前每个位置的贡献,所以对于上面的情况,令$c_{pre_{pre_i}}--,c_{pre_i}++$,因为这时之前要求$l\leq pre_{pre_i}$,现在只用$l\leq pre_i$了。

如果$r=i$,那么答案就是$\sum_{i=l}^rc_i$。所以可以离线下来之后用树状数组维护。

 1 #include<bits/stdc++.h>
 2 #define Rint register int
 3 using namespace std;
 4 const int N = 2000003;
 5 int n, mx, m, a[N], num[N], lst[N], ans[N], now = 1;
 6 struct Query {
 7     int l, r, id;
 8     inline bool operator < (const Query &o) const {return r < o.r;} 
 9 } q[N];
10 int c[N];
11 inline int lowbit(int x){return x & -x;}
12 inline void change(int pos, int val){
13     while(pos <= n){
14         c[pos] += val;
15         pos += lowbit(pos);
16     }
17 }
18 inline int query(int pos){
19     int res = 0;
20     while(pos){
21         res += c[pos];
22         pos -= lowbit(pos);
23     }
24     return res;
25 }
26 int main(){
27     scanf("%d%d%d", &n, &mx, &m);
28     for(Rint i = 1;i <= n;i ++){
29         scanf("%d", a + i);
30         lst[i] = num[a[i]];
31         num[a[i]] = i;
32     }
33     for(Rint i = 1;i <= m;i ++){
34         scanf("%d%d", &q[i].l, &q[i].r);
35         q[i].id = i;
36     }
37     sort(q + 1, q + m + 1);
38     for(Rint i = 1;i <= m;i ++){
39         while(now <= q[i].r){
40             if(lst[now]) change(lst[now], 1);
41             if(lst[lst[now]]) change(lst[lst[now]], -1);
42             ++ now;
43         }
44         ans[q[i].id] = query(q[i].r) - query(q[i].l - 1);
45     }
46     for(Rint i = 1;i <= m;i ++)
47         printf("%d\n", ans[i]);
48 }
Luogu4113

 

posted @ 2019-04-18 18:15  mizu164  阅读(131)  评论(0编辑  收藏  举报