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 }
  • 相关阅读:
    TCP三次握手原则
    IDEA快捷键总结
    长连接和短连接,单工、半双工和全双工
    Spring中神奇@aotuWrited
    有状态和无状态的对象区别
    GC--垃圾收集器
    SpringMVC工作原理
    java的对象锁和类锁
    oracle中的数据类型
    oracle中的函数
  • 原文地址:https://www.cnblogs.com/drurry/p/9094548.html
Copyright © 2011-2022 走看看