【牛客小白月赛17】G - 区间求和【莫队】

题目大意:

题目链接:https://ac.nowcoder.com/acm/contest/1085/G
小sun最近突然对区间来了兴趣,现在他有这样一个问题想问问你:
给你n个数,每个数为 a i a_i ai ,现在有 m m m个询问,每个询问 l , r l,r l,r,需要求出:
∑ i = l r a i × n u m ( a i ) \sum^{r}_{i=l}a_i\times num(a_i) i=lrai×num(ai)
其中 n u m ( a i ) num(a_i) num(ai)代表 a i a_i ai在这个区间中出现的次数。
你能帮帮他吗?


思路:

对于区间 [ l , r ] [l,r] [l,r]的每一个数字 x x x,一共有 n u m ( x ) num(x) num(x) x x x,每一个 x x x的贡献是 x × n u m ( x ) x\times num(x) x×num(x)。所以数字 x x x的贡献是 x × n u m ( x ) 2 x\times num(x)^2 x×num(x)2
题目中保证了 1 ≤ n , m , a i ≤ 1 0 5 1\leq n,m,a_i\leq 10^5 1n,m,ai105,而且没有强制在线,明显就是一道莫队的板子题了。
而且这道题和小Z的袜子还很像,改一下就可以了。


代码:

#include 
#include 
#include 
using namespace std;
typedef long long ll;

const int N=100010;
int n,m,T,l,r,a[N],pos[N];
ll ans[N],sum,s[N];

struct Ask
{
	int l,r,id;
}ask[N];

bool cmp(Ask x,Ask y)
{
	if (pos[x.l]<pos[y.l]) return 1;
	if (pos[x.l]>pos[y.l]) return 0;
	return x.r<y.r;
}

void add(int x) 
{
	sum-=s[a[x]]*s[a[x]]*a[x];
	s[a[x]]++;
	sum+=s[a[x]]*s[a[x]]*a[x];
}

void dev(int x)
{
	sum-=s[a[x]]*s[a[x]]*a[x];
	s[a[x]]--;
	sum+=s[a[x]]*s[a[x]]*a[x];
}

int main()
{
	scanf("%d%d",&n,&m);
	T=(int)sqrt(m);
	if (m%T) T++;
	for (int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d",&ask[i].l,&ask[i].r);
		ask[i].id=i;
		pos[i]=(i-1)/T+1;
	}
	sort(ask+1,ask+1+m,cmp);
	l=1;
	for (int i=1;i<=m;i++)
	{
		for (;l<ask[i].l;l++) dev(l);
		for (;l>ask[i].l;l--) add(l-1);
		for (;r<ask[i].r;r++) add(r+1);
		for (;r>ask[i].r;r--) dev(r);
		ans[ask[i].id]=sum;
	}
	for (int i=1;i<=m;i++)
		printf("%lld\n",ans[i]);
	return 0;
}

你可能感兴趣的:(莫队)