• BZOJ3781: 小B的询问


    【传送门:BZOJ3781


    简要题意:

      给出n个数a[i],有k种数,m个询问,每个询问输入l,r,输出$sum_{k}^{i=1}c[i]^2$,c[i]表示数字i在l到r中出现的次数


    题解:

      莫队(非常明显)

      直接分块,设sum[i]为当前l到r之间数字为i出现的个数,对于处理l和r的位置,只要将sum处理一下,把ans也处理一下就行了


    参考代码:

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long LL;
    int a[51000];
    struct qn
    {
        int l,r,id;
        LL d;
    }q[51000];
    int bk[51000];
    bool cmp(qn n1,qn n2)
    {
        return bk[n1.l]==bk[n2.l]?n1.r<n2.r:bk[n1.l]<bk[n2.l];
    }
    bool cmpd(qn n1,qn n2)
    {
        return n1.id<n2.id;
    }
    LL sum[51000],ans;
    void update(int x,int ad)
    {
        ans-=sum[a[x]]*sum[a[x]];
        sum[a[x]]+=ad;
        ans+=sum[a[x]]*sum[a[x]];
    }
    int main()
    {
        int n,m,k;
        scanf("%d%d%d",&n,&m,&k);
        int block=int(sqrt(n));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            bk[i]=(i-1)/block+1;
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&q[i].l,&q[i].r);
            q[i].id=i;
        }
        sort(q+1,q+m+1,cmp);
        int l=1,r=0;
        ans=0;
        memset(sum,0,sizeof(sum));
        for(int i=1;i<=m;i++)
        {
            while(l<q[i].l){update(l,-1);l++;}
            while(l>q[i].l){update(l-1,1);l--;}
            while(r<q[i].r){update(r+1,1);r++;}
            while(r>q[i].r){update(r,-1);r--;}
            q[i].d=ans;
        }
        sort(q+1,q+m+1,cmpd);
        for(int i=1;i<=m;i++) printf("%lld
    ",q[i].d);
        return 0;
    }

     

  • 相关阅读:
    C#的默认访问权限
    隐藏基类成员
    索引指示器的重载
    索引指示器
    vector
    string 函数
    细胞个数
    计蒜客 T1096 石头剪刀布
    计蒜客 T1125 判断字符串是否为回文
    计蒜客 T1152 成绩排序
  • 原文地址:https://www.cnblogs.com/Never-mind/p/8192924.html
Copyright © 2020-2023  润新知