题目大意:有一个长度为n的数组,m次询问,求一个区间内每一个数出现的次数的平方和
1<=n,m<=5e4
思路:用尺取的方法,我们先把所有要查询的区间的信息都保存下来,然后按照右端点从小到大优先,左端点其次的顺序排序,然后建立双指针l=1,r=0,对于每一个区间,先将当前r指针向右移直到与该区间右端点重合,再将左端点向右移直到与重合,移动端点的同时维护当前的sum,在本题中,对于1,4,9...这样的数列,每个元素之间相差2i-1,向右移动时就要加上2*a[r]出现次数-1,向左移时减去2*a[l]出现次数-1,重合之后自然就得到了当前区间的答案,而这样操作的时间复杂度和尺取一样也是O(n)的。
#include<bits/stdc++.h>
using namespace std;
const int N = 5e4 + 5;
int a[N], b[N];
struct inter
{
int l, r, id, pos;
bool operator < (const inter &x)const
{
if(pos==x.pos)
{
return r<x.r;
}
else
{
return pos < x.pos;
}
}
}mo[N];
long long ans[N];
int main()
{
int n, m, k;
cin >> n >> m >> k;
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
}
int size = (int)sqrt(n);
for (int i = 1; i <= m; i++)
{
scanf("%d%d", &mo[i].l, &mo[i].r);
mo[i].id = i;
mo[i].pos = (mo[i].l - 1) / size + 1;
}
sort(mo + 1, mo + m + 1);
int l = 1, r = 0;
long long anss = 0;
for (int i = 1; i <= m; i++)
{
while (l > mo[i].l)
{
l--;
b[a[l]]++;
anss += 2 * b[a[l]] - 1;//1,4,9...之间的差为2i-1
}
while (r < mo[i].r)
{
r++;
b[a[r]]++;
anss += 2 * b[a[r]] - 1;
}
while (l < mo[i].l)
{
b[a[l]]--;
anss -= 2 * b[a[l]] + 1;
l++;
}
while (r > mo[i].r)
{
b[a[r]]--;
anss -= 2 * b[a[r]] + 1;
r--;
}
ans[mo[i].id] = anss;
}
for (int i = 1; i <= m; i++)
{
printf("%lld\n", ans[i]);
}
return 0;
}