Group 2013 Multi-University Training 4

题意:给出n个从1~n的数字(唯一不重复)

   给出m个区间,问区间内最少有多少个不同的小组 

      小组:这个区间内的数是连续的,比如:1 3 2

思路:可以考虑线段树跟莫队

  我们这里用莫队来解

    这里用到一个vis数组来标记是否出现过

      增加某数字,就看看这个数字左右两边的数是否存在,再另作判断;

      删除某数字,同上

    但是有一点需要注意的是,倘若出现区间(2,3)到(4,5)这样的改变  假如我们先移动L ,就会先出现(4,3)这样一个区间,这样影响了答案的正确性

    但是我们如果把有关R的移动放前面就可以了嘛?

      不,假如有(4,5)到(2,3)这样的改变的话呢,我们就会造成(4,3)的局面;

    所以我们需要排除这两种情况

      当出现这些情况的时候呢,我们就将这个区间内的数给清空,然后add上这个区间内的一个数即可

        那么这样会不会增加复杂度呢?

          不会,因为即使不这样做,原本的区间也要走过这段路,所耗费的复杂度大致相同

            所以,是可行的

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=2e5+10;
 4 int vis[maxn];
 5 int a[maxn];
 6 int temp;  //记录每一个区间的不同数的总数;
 7 int block; //分块
 8 int ans[maxn];
 9 struct node
10 {
11     int l,r,id;
12 }query[maxn];
13 bool cmp(node x,node y)
14 {
15     if(x.l/block!=y.l/block)  //先按L分块,从小到大分
16         return x.l<y.l;
17     if(x.l/block&1)    //再按R来排序,根据该区间为奇数或是偶数,
18         return x.r<y.r;  //来确定从大到小排还是从小到大
19     return x.r>y.r;
20 }
21 void add(int x)
22 {
23     if(!vis[x-1]&&!vis[x+1]) temp++;
24     if(vis[x-1]&&vis[x+1]) temp--;
25     vis[x]=1;
26 }
27 void Delete(int x)
28 {
29     if(vis[x-1]&&vis[x+1]) temp++;
30     if(!vis[x-1]&&!vis[x+1]) temp--;
31     vis[x]=0;
32 }
33 int main()
34 {
35     int T;
36     scanf("%d",&T);
37     while(T--){
38         memset(vis,0,sizeof(vis));
39         int n,q;
40         scanf("%d%d",&n,&q);
41         for(int i=1;i<=n;i++) scanf("%d",&a[i]);
42         for(int i=1;i<=q;i++){
43             scanf("%d%d",&query[i].l,&query[i].r);
44             query[i].id=i;
45         }
46         block=sqrt(n);
47         sort(query+1,query+1+q,cmp);
48         int left=1,right=0;
49         temp=0;
50         for(int i=1;i<=q;i++){
51             if(right<query[i].l){
52                 for(int k=left;k<=right;k++)
53                     vis[a[k]]=0;
54                 temp=1;
55                 vis[a[query[i].l]]=1;
56                 left=right=query[i].l;
57             }
58             if(left>query[i].r){
59                 for(int k=left;k<=right;k++)
60                     vis[a[k]]=0;
61                 temp=1;
62                 vis[a[query[i].r]]=1;
63                 left=right=query[i].r;
64 
65             }
66             while(right<query[i].r)add(a[++right]);
67             while(right>query[i].r)Delete(a[right--]);
68             //同理
69             //如果左端点小于询问区间,则需要将不在区间内的点给删除
70             while(left<query[i].l) Delete(a[left++]);
71             //如果左端点大于询问区间,则需要将为纳入区间但需要纳入区间的点给加进来
72             while(left>query[i].l) add(a[--left]);
73             //同理
74             //记录答案
75             ans[query[i].id]=temp;
76         }
77         for(int i=1;i<=q;i++)
78             printf("%d\n",ans[i]);  //输出答案
79     }
80     return 0;
81 }

 

posted @ 2020-03-21 09:40  古比  阅读(124)  评论(0)    收藏  举报