洛谷 P2709 - 小B的询问(莫队算法)

题目描述

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

输入输出格式

输入格式:

第一行,三个整数N、M、K。

第二行,N个整数,表示小B的序列。

接下来的M行,每行两个整数L、R。

输出格式:

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

输入输出样例

输入样例#1: 

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

输出样例#1: 

6
9
5
2

说明

对于全部的数据,1<=N、M、K<=50000

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define clr(a) memset(a,0,sizeof(a))

const int MAXN = 1e5+10;
const int INF = 0x3f3f3f3f;
const int N = 50001;
int n,m,k;
int ll,rr;
int tong[N];//记录a[i]出现的次数
int an[N];
int a[N];//原顺序数组
LL kind;
struct node{
    int l,r,num;
    int id;//分块后块的编号
}q[N];
void del(int p){//del表示删除,ins加入,kind表示当前答案
    kind -= 2 * (--tong[a[p]]) + 1;
}
void ins(int p){
    kind += 2 * (++tong[a[p]]) - 1;
}
bool cmp(node a,node b){
    if(a.id == b.id)    return a.r < b.r;//块号相同则按照右界从小到大排序
    else return a.id < b.id ;//块号不同则按照块号从小到大排序
}
int main(){
    scanf("%d%d%d",&n,&m,&k);
    int len = sqrt(n);//分块莫队
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&q[i].l,&q[i].r);
        q[i].id = q[i].l / len;//记录块号
        q[i].num = i;
    }
    sort(q+1,q+m+1,cmp);//排序预处理
    ll = q[1].l;
    rr = q[1].l - 1;//开始没有点
    for(int i=1;i<=m;i++){
        while(ll<q[i].l)del(ll++);
        while(ll>q[i].l)ins(--ll);
        while(rr>q[i].r)del(rr--);
        while(rr<q[i].r)ins(++rr);//增加点/减少点
        an[q[i].num] = kind;//记录答案
    }
    for(int i=1;i<=m;i++)
        cout<<an[i]<<endl;
}

猜你喜欢

转载自blog.csdn.net/l18339702017/article/details/81482633