洛谷Oj-P2709 小B的询问-莫队

版权声明:欢迎转载 https://blog.csdn.net/suntengnb/article/details/79947832

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

struct query
{
    int l;
    int r;
    int id;//第id个查询
    int b;//块的编号
    long long ans;//答案可能会爆int
};
query q[50010];
int a[50010];//原数组
int n,m,k;//k其实没啥用
int curL,curR;
int cnt[50010];
long long sum;//求题目中要求的和
bool cmp1(const query &a,const query &b)//分块
{
    if(a.b == b.b)
        return a.r < b.r;
    return a.b < b.b;
}
bool cmp2(const query &a,const query &b)//对查询重新排序
{
    return a.id < b.id;
}
void add(int id)
{
    sum += 2 * cnt[a[id]] + 1;//加上贡献
    cnt[a[id]]++;//注意与上句的顺序不能颠倒
    return;
}
void del(int id)
{
    sum += -2 * cnt[a[id]] + 1;//减去损失
    cnt[a[id]]--;//注意与上句的顺序不能颠倒
    return;
}
inline void solve(query &q)//莫队
{
    int l = q.l;
    int r = q.r;
    while(curL < l)
        del(curL++);
    while(curL > l)
        add(--curL);
    while(curR < r)
        add(++curR);
    while(curR > r)
        del(curR--);
    q.ans = sum;
}
int main()
{
    cin >> n >> m >> k;
    int sz = 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].b = q[i].l / sz;//块的编号
        q[i].id = i;
    }
    sort(q + 1,q + m + 1,cmp1);//分块
    //初始化左右指针
    curL = 1;
    curR = 0;
    for(int i = 1; i <= m; ++i)
    {
        //sum = 0;//重置是不对的,因为都说了莫队要利用历史信息
        solve(q[i]);
    }
    sort(q + 1,q + m + 1,cmp2);//恢复查询顺序
    for(int i = 1; i <= m; ++i)//输出
        cout << q[i].ans << endl;
    return 0;
}

解决方法:
c(i)的定义我们可以判断这道题需要用莫队来求解,抱着试一试的心态写了一发无脑求答案的莫队,然后大部分点都T了。数据规模为1<=N、M、K<=50000,极限情况下有50000次查询,每次查询都要求50000次和,那时间上自然是过不去的。
我是这样求答案的:

long long sigma()
{
    long long sum = 0;
    for(int i = 1; i <= k; ++i)//从1到k
        sum += cnt[i] * cnt[i];
    return sum;
}

其实sum完全没有必要每次都重算,对sum的修改可以在add和del函数中实现
对于某数a,它从出现b次增加到了b+1次,之前它对sum的贡献为b2,现在它对答案的贡献为(b+1)2=b2+2b+1,贡献增加了2b+1,所以在写add函数时只需要将增加的贡献加到sum上即可,出现次数减少的情况同上。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页