我们设 col[i] 表示当前颜色 i 出现了多少次,ans 表示当前共有多少种可行的配对方案(有多少种可以选到一双颜色相同的袜子)。然后每次移动的时候更新答案:设当前颜色为 k,如果是增长区间就是 ans 加上 (2col[k]+1)−(2col[k]);如果是缩短就是 ans 减去 (2col[k])−(2col[k]−1)。这个询问的答案就是 (2r−l+1)ans。
#include <algorithm>#include <cmath>#include <iostream>using namespace std;constexpr int N = 50005;int n, m, maxn;int c[N];long long sum;int cnt[N];long long ans1[N], ans2[N];struct query { int l, r, id; bool operator<(const query &x) const { // 重载<运算符 if (l / maxn != x.l / maxn) return l < x.l; return (l / maxn) & 1 ? r < x.r : r > x.r; }} a[N];void add(int i) { sum += cnt[i]; cnt[i]++;}void del(int i) { cnt[i]--; sum -= cnt[i];}long long gcd(long long a, long long b) { return b ? gcd(b, a % b) : a; }int main() { cin.tie(nullptr)->sync_with_stdio(false); cin >> n >> m; maxn = sqrt(n); for (int i = 1; i <= n; i++) cin >> c[i]; for (int i = 0; i < m; i++) cin >> a[i].l >> a[i].r, a[i].id = i; sort(a, a + m); for (int i = 0, l = 1, r = 0; i < m; i++) { // 具体实现 if (a[i].l == a[i].r) { ans1[a[i].id] = 0, ans2[a[i].id] = 1; continue; } while (l > a[i].l) add(c[--l]); while (r < a[i].r) add(c[++r]); while (l < a[i].l) del(c[l++]); while (r > a[i].r) del(c[r--]); ans1[a[i].id] = sum; ans2[a[i].id] = (long long)(r - l + 1) * (r - l) / 2; } for (int i = 0; i < m; i++) { if (ans1[i] != 0) { long long g = gcd(ans1[i], ans2[i]); ans1[i] /= g, ans2[i] /= g; } else ans2[i] = 1; cout << ans1[i] << '/' << ans2[i] << '\n'; } return 0;}
普通莫队的优化
过程
我们看一下下面这组数据
// 设块的大小为 2 (假设)1 12 1003 14 100
手动模拟一下可以发现,r 指针的移动次数大概为 300 次,我们处理完第一个块之后,l=2,r=100,此时只需要移动两次 l 指针就可以得到第四个询问的答案,但是我们却将 r 指针移动到 1 来获取第三个询问的答案,再移动到 100 获取第四个询问的答案,这样多了九十几次的指针移动。我们怎么优化这个地方呢?这里我们就要用到奇偶化排序。
什么是奇偶化排序?奇偶化排序即对于属于奇数块的询问,r 按从小到大排序,对于属于偶数块的排序,r 从大到小排序,这样我们的 r 指针在处理完这个奇数块的问题后,将在返回的途中处理偶数块的问题,再向 n 移动处理下一个奇数块的问题,优化了 r 指针的移动次数,一般情况下,这种优化能让程序快 30% 左右。
实现
排序代码:
[list2tab]
压行
// clang-format off// 这里有个小细节等下会讲int unit; // 块的大小struct node { int l, r, id; bool operator < (const node &x) const { return l / unit == x.l / unit ? (r == x.r ? 0 : ((l / unit) & 1) ^ (r < x.r)) : l < x.l; }};
不压行
struct node { int l, r, id; bool operator<(const node &x) const { if (l / unit != x.l / unit) return l < x.l; // 注意下面两行不能写小于(大于)等于,否则会出错(详见下面的小细节) if ((l / unit) & 1) return r < x.r; return r > x.r; }};