【洛谷P2709】小B的询问

题目链接

小B的询问

题目描述

小B有一个序列,包含\(N\)\(1\)~\(K\)之间的整数。他一共有\(M\)个询问,每个询问给定一个区间\([L..R]\),求\(\sum^{K}_{i=1}{c(i)^2}\)的值,其中i的值从\(1\)\(K\),其中\(c(i)\)表示数字\(i\)\([L..R]\)中的重复次数。小B请你帮助他回答询问。

输入格式

第一行,三个整数\(N\)\(M\)\(K\)
第二行,\(N\)个整数,表示小B的序列。
接下来的\(M\)行,每行两个整数\(L\)\(R\)

输出格式

\(M\)行,每行一个整数,其中第\(i\)行的整数表示第\(i\)个询问的答案。

样例输入

6 4 3
1 3 2 1 1 3
1 4
2 6
3 5
5 6

样例输出

6
9
5
2

说明/提示

对于全部的数据,\(1 \le N\)\(M\)\(K \le 50000\)

题解

一道莫队板题(说不定也可以用其他方法做,但是我太菜了,只会莫队)。
我们用\(cnt[j]\)\(l,r\)维护\(l\)\(r\)区间内\(j\)这个数字的个数,并且维护一个\(ans\)值,然后我们移动\(l\)\(r\)的时候快速地维护这个数组和\(ans\)值(\(ans\)值先减去原本的值的平方,再加上新的值的平方)。
把式子化简之后就是:

  • 如果\(cnt_i\)多了一个,那\(ans+=2*cnt[x]+1\)
  • 如果\(cnt_i\)少了一个,那\(ans-=2*cnt[x]-1\)

然后我们把询问中\(\frac {l}{\sqrt{n}}\)相同的数分为一组(这里分块的大小是\(\sqrt{n}\),一般来说随机数据的话区\(\sqrt{n}\)会比较好,但是不同的题目分块的大小也可以不同)
然后在同一组内的询问,我们按照询问的\(r\)值从小到大排序。
这样排序后暴搜的时间复杂度就变成了\(O(n\sqrt{n})\)了,能过此题。
上代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,k;
int a[1000009];
struct aa{
    int l,r,x,ans;
}p[1000009];
int sq;
bool cmp(aa x,aa y){
    if(x.l/sq<y.l/sq) return 1;
    if(x.l/sq>y.l/sq) return 0;
    return x.r<y.r;
}
int cnt[1000009];
bool cmpp(aa x,aa y){return x.x<y.x;}
int main(){
    scanf("%d%d%d",&n,&m,&k);
    sq=sqrt(n);
    for(int j=1;j<=n;j++)
        scanf("%d",&a[j]);
    for(int j=1;j<=m;j++){
        scanf("%d%d",&p[j].l,&p[j].r);
        p[j].x=j;
    }
    sort(p+1,p+m+1,cmp);
    int ll=1,rr=1,ss=1;
    cnt[a[1]]=1;
    for(int j=1;j<=m;j++){
        while(rr<p[j].r){
            rr++;
            ss-=cnt[a[rr]]*cnt[a[rr]];
            cnt[a[rr]]++;
            ss+=cnt[a[rr]]*cnt[a[rr]];
        }
        while(ll>p[j].l){
            ll--;
            ss-=cnt[a[ll]]*cnt[a[ll]];
            cnt[a[ll]]++;
            ss+=cnt[a[ll]]*cnt[a[ll]];
        }
        while(rr>p[j].r){
            ss-=cnt[a[rr]]*cnt[a[rr]];
            cnt[a[rr]]--;
            ss+=cnt[a[rr]]*cnt[a[rr]];
            rr--;
        }
        while(ll<p[j].l){
            ss-=cnt[a[ll]]*cnt[a[ll]];
            cnt[a[ll]]--;
            ss+=cnt[a[ll]]*cnt[a[ll]];
            ll++;
        }
        p[j].ans=ss;
    }
    sort(p+1,p+m+1,cmpp);
    for(int j=1;j<=m;j++)
        printf("%d\n",p[j].ans);
    return 0;
}
posted @ 2019-10-30 14:58  oblivionl  阅读(127)  评论(0编辑  收藏  举报