天天看点

Yandex.Algorithm 2011 Round 2-D - Powerful array-莫队算法(分块算法)

http://codeforces.com/contest/86/problem/D

莫队算法就是处理这类能O1从区间【l,r】得到 【l-1,r】【l,r+1】,不带修改的区间查询的一类问题。

题目要求任意 subarray 的 cnt[a[i]]*cnt[a[i]]*a[i]

按照套路,把所有区间按 左端点 所在块作第一关键字,右端点第二关键字 排序

遍历,预处理第一次询问后,每次询问暴力求解,转移如下:

  res -= cnt[a[i]]*cnt[a[i]]*a[i];

            cnt[a[i]]++;

            res += cnt[a[i]]*cnt[a[i]]*a[i];

算法复杂度为q*sqrt n 

考虑右指针,先是按照递增顺序移动,假设当前块右指针在最右端,当下一块时,需移动到最左端,那么也就是每跨越一个块,最多移动O(N),总共sqrtn个块,复杂度n*sqrtn

考虑左指针如果在同一块,移动量不超过sqrtn【在一块内嘛】

如果是跨越一块,移动量最多不超过2*sqrtn【块长只有sqrtn】

复杂度(q*sqrtn)

总复杂度(q+n)*sqrtn

----------------------------------------------------------

有一点 add move 的时候直接那刚才的暴力转移会超时,

因为每次变化只有1, 也就是 x*x*a 变成 (x+1)*(x+1)*a,显然我们直接加上 a*(2*x+1)就好了。。。最后跑了 1.9S

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;
#define N 200100
typedef long long ll;
int a[N], cnt[N*5];
ll  ans[N], res;
int L, R;

struct node
{
    int x, y, l, p;
} q[N];
bool cmp(const node &x, const node &y)
{
    if (x.l == y.l) return x.y < y.y;
    return x.l < y.l;
}
void query(int x, int y, int flag)
{
    if (flag)
    {
        for (int i=x; i<L; i++)
        {
            res += (cnt[a[i]]*2+1)*a[i];
            cnt[a[i]]++;
        }
        for (int i=L; i<x; i++)
        {
            res += (-cnt[a[i]]*2+1)*a[i];
            cnt[a[i]]--;
        }
        for (int i=y+1; i<=R; i++)
        {
            res += (-cnt[a[i]]*2+1)*a[i];
            cnt[a[i]]--;
        }
        for (int i=R+1; i<=y; i++)
        {
            res += (cnt[a[i]]*2+1)*a[i];
            cnt[a[i]]++;
        }

    }
    else
    {

        for (int i=x; i<=y; i++)
        {
            res += (cnt[a[i]]*2+1)*a[i];
            cnt[a[i]]++;
        }
    }
    L = x, R = y;
}
int main()
{
    int n, t;

    scanf("%d%d", &n, &t);
    for (int i=1; i<=n; i++) scanf("%d", &a[i]);
    int block_size = sqrt(1.0*n);

    for (int i=0; i<t; i++)
    {
        scanf("%d%d", &q[i].x, &q[i].y);
        q[i].l = q[i].x / block_size;
        q[i].p = i;
    }
    sort(q, q+t, cmp);

    res = 0;
    for (int i=0; i<t; i++)
    {
        query(q[i].x, q[i].y, i);
        ans[q[i].p] = res;
    }

    for (int i=0; i<t; i++) printf("%I64d\n", ans[i]);

    return 0;
}