P2709 小B的询问 查询莫队
解题思路
本题需要使用莫队算法来高效处理多个区间查询。题目要求计算每个查询区间内所有数字出现次数的平方和。具体步骤如下:
-
莫队算法框架:将查询分块排序,通过移动区间指针来高效处理多个查询
-
平方和维护:动态维护当前区间内各数字出现次数的平方和
-
指针移动处理:在移动区间指针时,实时更新平方和
-
值域过滤:只统计值域在[1,k]范围内的数字
代码注释
#include<bits/stdc++.h> #define ll long long // 定义long long为ll,方便使用 using namespace std; const int N = 1e5 + 10; // 定义最大数据范围 // 查询结构体:存储每个查询的左右端点和查询编号 struct node { int l, r, id; }; node t[N]; // 存储所有查询 // 变量定义 int n, m, k, bcnt, a[N], cnt[N]; // n序列长度,m查询数,k值域上限,bcnt块大小,a序列,cnt计数数组 ll sum, ans[N]; // sum当前平方和,ans存储每个查询的答案 int L = 1, R; // 当前区间指针[L,R] // 查询排序比较函数 bool cmp(node a, node b) { // 按块排序,同一块内按右端点排序 if(a.l / bcnt == b.l / bcnt) return a.r < b.r; else return a.l < b.l; } // 从当前区间移除位置pos的数字 void del(int pos) { if(a[pos] <= k) { // 只处理值域范围内的数字 sum -= (ll)cnt[a[pos]] * cnt[a[pos]]; // 先减去旧的平方值 cnt[a[pos]]--; // 减少计数 sum += (ll)cnt[a[pos]] * cnt[a[pos]]; // 加上新的平方值 } else { cnt[a[pos]]--; // 超出值域的数字只更新计数 } } // 添加位置pos的数字到当前区间 void add(int pos) { if(a[pos] <= k) { // 只处理值域范围内的数字 sum -= (ll)cnt[a[pos]] * cnt[a[pos]]; // 先减去旧的平方值 cnt[a[pos]]++; // 增加计数 sum += (ll)cnt[a[pos]] * cnt[a[pos]]; // 加上新的平方值 } else { cnt[a[pos]]++; // 超出值域的数字只更新计数 } } int main() { // 输入数据 scanf("%d%d%d", &n, &m, &k); bcnt = sqrt(n); // 计算块大小 // 输入序列 for(int i = 1; i <= n; i++) scanf("%d", &a[i]); // 输入查询 for(int i = 1; i <= m; i++) { scanf("%d%d", &t[i].l, &t[i].r); t[i].id = i; // 记录查询编号 } // 对查询进行排序 sort(t + 1, t + 1 + m, cmp); // 处理每个查询 for(int i = 1; i <= m; i++) { int tl = t[i].l, tr = t[i].r, tid = t[i].id; // 调整区间指针L while(L < tl) del(L++); // 左指针右移,删除左边元素 while(L > tl) add(--L); // 左指针左移,添加左边元素 // 调整区间指针R while(R < tr) add(++R); // 右指针右移,添加右边元素 while(R > tr) del(R--); // 右指针左移,删除右边元素 // 存储当前查询的答案 ans[tid] = sum; } // 输出结果 for(int i = 1; i <= m; i++) printf("%lld\n", ans[i]); return 0; }

浙公网安备 33010602011771号