P2709 莫队
小B的询问
题目描述
小B 有一个长为 \(n\) 的整数序列 \(a\),值域为 \([1,k]\)。
他一共有 \(m\) 个询问,每个询问给定一个区间 \([l,r]\),求:
\[\sum\limits_{i=1}^k c_i^2
\]
其中 \(c_i\) 表示数字 \(i\) 在 \([l,r]\) 中的出现次数。
小B请你帮助他回答询问。
输入格式
第一行三个整数 \(n,m,k\)。
第二行 \(n\) 个整数,表示 小B 的序列。
接下来的 \(m\) 行,每行两个整数 \(l,r\)。
输出格式
输出 \(m\) 行,每行一个整数,对应一个询问的答案。
样例 #1
样例输入 #1
6 4 3
1 3 2 1 1 3
1 4
2 6
3 5
5 6
样例输出 #1
6
9
5
2
提示
【数据范围】
对于 \(100\%\) 的数据,\(1\le n,m,k \le 5\times 10^4\)。
莫队
将查询区间离线 首先按照区间左端点所属的块排序
若左端点所属块相同 那么按右端点排序
奇偶性优化 loc[x.l]&1 则 return x.r>y.r else 则 return x.r<y.r
然后双指针遍历统计即可 注意
我们Add的时候当前点是处理过的(已经Add了)所以先L-- R++ 再Add
我们Delete的时候当前点还没有删 所以先Delete 再 L++ R--
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+5;
int n,m,k;
int len,loc[N],a[N],cnt[N],Ans=0,tot,ans[N];
struct md { //莫队
int l,r,id;
} q[N];
inline bool cmp(md x,md y) {
if(loc[x.l]==loc[y.l]) {
if(loc[x.l]&1)return x.r>y.r;
return x.r<y.r;
}
return loc[x.l]<loc[y.l];
}
inline void Add(int x) {
cnt[x]++;
// if(cnt[x]==1)Ans++;
Ans+=(cnt[x]<<1)-1;
}
inline void Delete(int x) {
cnt[x]--;
// if(cnt[x]==0)Ans--;
Ans-=(cnt[x]<<1|1);
}
signed main() {
ios::sync_with_stdio(false);
cin>>n>>m>>k;
len=sqrt(n);
tot=n/len;
tot+=(n%len!=0);
for(int i=1; i<=n; i++)loc[i]=(i-1)/len+1;
for(int i=1; i<=n; i++)cin>>a[i];
int l,r;
for(int i=1; i<=m; i++)
cin>>l>>r,q[i].l=l,q[i].r=r,q[i].id=i;
sort(q+1,q+m+1,cmp);
int L=1,R=0;
for(int i=1; i<=m; i++) {
while(L>q[i].l)L--,Add(a[L]);
while(R<q[i].r)R++,Add(a[R]);
while(L<q[i].l)Delete(a[L]),L++;
while(R>q[i].r)Delete(a[R]),R--;
ans[q[i].id]=Ans;
}
for(int i=1; i<=m; i++)cout<<ans[i]<<"\n";
return 0;
}