BZOJ-3781: 小B的询问

[文章目录]

Description

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

潜心研究一发莫队。

首先,针对于区间的题目,并且是我们能通过[l,r]计算出[l-1,r]和[l+1,r],[l,r+1],[l,r-1]的题目。假如我们暴力的话,直接双指针跑啊,从上一个询问的[l1,r1],跑到[l2,r2],时间复杂度O()

【f(n)为由一个区间计算另一个区间的时间复杂度】,而莫队就是先将询问离线排个序,使得每次询问的出入不是那么大而降低复杂度。@ 一篇好博客

莫队的策略:

先按左端点分块,(1~√n)第一块,(√n~2√n)第二块...,使得每个块里左节点最大可移动幅度不超过√n。代码实现上直接左端点除以√n得出的值+1就是所在块的编号。然后每个块分别求解,每个块按照右端点排序,保证右端点的移动是递增的,所以每个块内右端点移动的复杂度是O(n)的,总共有√n块,所以时间O(),然后每个块内的左端点最大幅度也是√n,所以每个询问都是√n,时间,O(),然后块与块之间相连的初始化直接O(n)跑,会初始化√n块,时间O(),再加上排序O(m*logm),所以按照这种计算方式总时间复杂度O()

然后,根据广大人民意愿就当成是O()的吧。【实际上也差不多了好吧】233

今天没时间了,题目明天写吧。(实际上由于是土豪题,拖了大概6.7~8.3。。。两个月啊。233)以下是代码。
直接修改答案。

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m,k,col[51000],num[51000];
struct quest
{
    int l,r,bl,id,ans;
}a[51000];
bool cmp1(quest x,quest y)
{
    return x.bl==y.bl? x.r<y.r : x.bl<y.bl;
}
bool cmp2(quest x,quest y)
{
    return x.id<y.id;
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++)
        scanf("%d",&col[i]);
    int blo=sqrt(1.0*n)+1;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&a[i].l,&a[i].r);
        a[i].id=i; a[i].bl=a[i].l/blo;
    }
    sort(a+1,a+m+1,cmp1);
    int L=1,R=1,now=1; num[col[1]]++;
    for(int i=1;i<=m;i++)
    {
        while(L>a[i].l)
        {
            L--;
            now+=(num[col[L]]<<1)+1;
            num[col[L]]++;
        }
        while(R<a[i].r)
        {
            R++;
            now+=(num[col[R]]<<1)+1;
            num[col[R]]++;
        }
        while(L<a[i].l)
        {
            now-=(num[col[L]]<<1)-1;
            num[col[L]]--;
            L++;
        }
        while(R>a[i].r)
        {
            now-=(num[col[R]]<<1)-1;
            num[col[R]]--;
            R--;
        }
        a[i].ans=now;
    }
    sort(a+1,a+m+1,cmp2);
    for(int i=1;i<=m;i++)
        printf("%d\n",a[i].ans);
    return 0;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注