zoukankan      html  css  js  c++  java
  • [AHOI2013]作业

    题目描述

    此时己是凌晨两点,刚刚做了Codeforces的小A掏出了英语试卷。英语作业其实不算多,一个小时刚好可以做完。然后是一个小时可以做完的数学作业,接下来是分别都是一个小时可以做完的化学,物理,语文......小A压力巨大。

    这是小A碰见了一道非常恶心的数学题,给定了一个长度为n的数列和若干个询问,每个询问是关于数列的区间表示数列的第1个数到第r个数),首先你要统计该区间内大于等于a,小于等于b的数的个数,其次是所有大于等于a,小于等于b的,且在该区间中出现过的数值的个数。

    小A望着那数万的数据规模几乎绝望,只能向大神您求救,请您帮帮他吧。

    输入输出格式

    输入格式:

    第一行n,m

    接下来n个数表示数列

    接下来m行,每行四个数l,r,a,b

    输出格式:

    输出m行,分别对应每个询问,输出两个数,分别为在1到i?这段区间中大小在[a,b]中的数的个数,以及大于等于a,小于等于b的,且在该区间中出现过的数值的个数(具体可以参考样例)。

    输入输出样例

    输入样例#1:

     3 4

     1 2 2

     1 2 1 3

     1 2 1 1

     1 3 1 3

     2 3 2 3

    输出样例#1:
     2 2
     1 1
     3 2 
     2 1

    说明

      N<=100000,M<=100000

    暴力做法77分:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int n,m,c[100010];
     4 bool p[10000000];
     5 int main()
     6 {    
     7     scanf("%d%d",&n,&m);
     8     for(int i=1;i<=n;i++) scanf("%d",&c[i]);
     9     int l,r,a,b,ans,aans;
    10     while(m--)
    11     {
    12         ans=0;aans=0;
    13         scanf("%d%d%d%d",&l,&r,&a,&b);
    14         for(int i=l;i<=r;i++)
    15         if(c[i]>=a&&c[i]<=b)
    16         {
    17             ++ans;
    18             if(!p[c[i]]) p[c[i]]=1,++aans;
    19         }
    20         for(int i=l;i<=r;i++) p[c[i]]=0;
    21         printf("%d %d
    ",ans,aans);
    22     }
    23     return 0;
    24 }

    正解:

    做法不唯一,常见做法为莫队+分块或者莫队+树状数组,但是网上也有一些奇奇怪怪的方法。

    求任意一段区间内在[a,b]的数字个数,很容易想到分块;但是题目有要求该区间内在[a,b]的数值种数,这又很容易联想到莫队。所以正解就是莫队+分块。例如样例的第一个询问,莫队增加在这个区间内每个数字出现的次数、该数字所在的块的元素个数(包括重复的数字)、该数所在块的不重复元素个数。然后分块询问[a,b]之间在规定区间的答案。

    正解很巧妙地一点在于分块分的不仅仅是有多少个数(即n),还是数值(即color[ ])。这也就存在一个问题,假设n很小,但是color[ ]很大又要另做处理。

    如果不能理解代码的建议自行模拟样例。



    更新:针对hack数据改了一波,之前的问题是如果l或r超出了color[]的最大值,posi[l]或者posi[r]会 变成0,那么对答案的统计就会出现问题

     1 /*hack.in: 5 1 1 1 2 5 2 3 5 3 10   hack.out: 1 1*/
     2 #include<bits/stdc++.h>
     3 using namespace std;
     4 const int N=100010;
     5 int n,m,L,R,block,num,posi[N],lll[N],rrr[N],sum[N],skuai[N],change[N],color[N],ans1[N],ans2[N],anss1,anss2;
     6 struct node{
     7     int l,r,x,y,id;
     8 }a[N];
     9 bool cmp(node aa,node bb)
    10 {
    11     if(posi[aa.l]==posi[bb.l]) return aa.r<bb.r;
    12     else return aa.l<bb.l;
    13 }
    14 void build()//预处理出每个块的边界
    15 {
    16     block=sqrt(n);num=n/block;
    17     if(n%block) num++;
    18     for(int i=1;i<=num;i++) 
    19     lll[i]=(i-1)*block+1,rrr[i]=i*block;
    20     rrr[num]=n;
    21     for(int i=1;i<=n;i++) posi[i]=(i-1)/block+1;
    22 }
    23 void add(int col)
    24 {
    25     sum[col]++;skuai[posi[col]]++;
    26     if(sum[col]==1) change[posi[col]]++;
    27 }
    28 void del(int col)
    29 {
    30     sum[col]--;skuai[posi[col]]--;
    31     if(!sum[col]) change[posi[col]]--;
    32 }
    33 void find(int l,int r,int id)
    34 {
    35     if(posi[l]==posi[r]) 
    36     {
    37         for(int i=l;i<=r;i++) if(sum[i]) ans1[id]+=sum[i],ans2[id]++;
    38         return;
    39     }
    40     if(posi[l]!=0)
    41         for(int i=l;i<=rrr[posi[l]];i++) if(sum[i]) ans1[id]+=sum[i],ans2[id]++;
    42     if(posi[r]!=0)
    43         for(int i=lll[posi[r]];i<=r;i++) if(sum[i]) ans1[id]+=sum[i],ans2[id]++;
    44     if(posi[l]!=0&&posi[r]!=0)
    45     for(int i=posi[l]+1;i<posi[r];i++) ans1[id]+=skuai[i],ans2[id]+=change[i];
    46     if(posi[l]!=0&&posi[r]==0) 
    47     for(int i=posi[l]+1;i<=num;i++) ans1[id]+=skuai[i],ans2[id]+=change[i];
    48 }
    49 int main()
    50 {
    51     scanf("%d%d",&n,&m);
    52     for(int i=1;i<=n;i++) scanf("%d",&color[i]);
    53     build();
    54     for(int i=1;i<=m;i++)
    55     {
    56         scanf("%d%d%d%d",&a[i].l,&a[i].r,&a[i].x,&a[i].y);
    57         a[i].id=i;
    58     }
    59     L=1,R=0;
    60     sort(a+1,a+1+m,cmp);
    61     for(int i=1;i<=m;i++)
    62     {
    63         while(R<a[i].r) add(color[++R]);
    64         while(R>a[i].r) del(color[R--]);
    65         while(L<a[i].l) del(color[L++]);
    66         while(L>a[i].l) add(color[--L]);
    67         find(a[i].x,a[i].y,a[i].id);
    68     }
    69     for(int i=1;i<=m;i++)
    70         printf("%d %d
    ",ans1[i],ans2[i]);
    71     return 0;
    72 }
  • 相关阅读:
    mysql BETWEEN操作符 语法
    mysql IN操作符 语法
    mysql LIKE通配符 语法
    mysql TOP语句 语法
    mysql DELETE语句 语法
    mysql Update语句 语法
    mysql INSERT语句 语法
    mysql ORDER BY语句 语法
    mysql OR运算符 语法
    mysql AND运算符 语法
  • 原文地址:https://www.cnblogs.com/drurry/p/9094548.html
Copyright © 2011-2022 走看看