小B的询问

题目描述

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

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

题解

分块可以做,考虑怎么当端点移动时,怎么维护答案。

当区间扩张到i,只对c(i)有影响,所以只有c(i)的值变成(c(i)+1),那么答案就很好维护了,增加了2*c(i)+1

当区间缩短,同理只有c(i)变成(c(i)-1),所以减去(2*c(i)-1)

只要是记录一下莫队模板

#include<bits/stdc++.h>
using namespace std;

const int maxn=50005;
int n,m,k,ret;
int a[maxn],cnt[maxn];
int size,pos[maxn];
int ans[maxn];
struct question{
    int l,r,id;
}q[maxn];
template<class T>inline void read(T &x){
    x=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
}

bool cmp(question a,question b){
    if(pos[a.l]==pos[b.l]) return a.r<b.r;
    return pos[a.l]<pos[b.l];
}

void add(int x){
    ret+=2*cnt[a[x]]+1;
    cnt[a[x]]++;
}

void del(int x){
    ret-=2*cnt[a[x]]-1;
    cnt[a[x]]--;
}

int main(){
    read(n);read(m);read(k);
    size=sqrt(n+0.5);
    for(int i=1;i<=n;i++){read(a[i]);pos[i]=(i-1)/size+1;}
    for(int i=1;i<=m;i++){read(q[i].l);read(q[i].r);q[i].id=i;}
    sort(q+1,q+m+1,cmp);
    int nowl=1,nowr=0;
    for(int i=1;i<=m;i++){
        while(nowl>q[i].l) add(--nowl);
        while(nowr<q[i].r) add(++nowr);
        while(nowl<q[i].l) del(nowl++);
        while(nowr>q[i].r) del(nowr--);
        ans[q[i].id]=ret;
    }
    for(int i=1;i<=m;i++) printf("%d
",ans[i]);
}
View Code
原文地址:https://www.cnblogs.com/sto324/p/11234656.html