入门题
对于一个区间的询问,如果在已知\([l,r]\)的答案时可以用O(1)的时间求出左右端点\(±1\)的答案,就可以使用莫队来优化。
设已知区间为\([l_1,r_1]\),所求区间为\([l_2,r_2]\)
可知求得\([l_2,r_2]\)的成本是\(|l_1-l_2| + |r_1-r_2|\)如果把这两个区间看成点,这个成本就是两点的曼哈顿距离,
对于多个询问,求出曼哈顿距离最小生成树就可以以最小成本获得答案,使用一种奇怪的方式 ——sort来获得最优的转移方式 我并不知道原理
对于整个区间进行分块,每个询问按照块编号为第一关键字,右端点为第二关键字进行排序,然后依次处理每个询问即可。看起来还是很暴力
卡常技巧我仍然不知道为什么:块大小\(\frac{n}{\sqrt{m*\frac{2}{3}}}\)比\(\sqrt n\)更快,sort时对于同一块里的询问按照奇偶排序更快
效果图
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 50005; typedef long long LL;
struct Data{
int l, r, id, pos;
inline bool operator < (const Data & rhs) const {
return pos ^ rhs.pos ? pos < rhs.pos : pos&1?r<rhs.r:r>rhs.r;
// if (pos == rhs.pos) return r < rhs.r;
// return pos < rhs.pos;
}
}d[MAXN];
int b[MAXN], a[MAXN], n, m, k, cnt[MAXN];LL anss[MAXN];
int main(void) {
scanf("%d%d%d", &n, &m, &k);
int block = n/sqrt(m*2/3);
for(int i = 1; i <= n; ++i) scanf("%d", a+i);
for(int i = 1; i <= m; ++i) {
scanf("%d%d", &d[i].l, &d[i].r); d[i].id = i;
d[i].pos = (d[i].l-1)/block + 1;
}
sort(d+1, d+m+1);
int l = 1, r = 0; LL ans = 0;
for(int i = 1; i <= m; ++i) {
while (l > d[i].l) l--, cnt[a[l]]++, ans += 2*cnt[a[l]] - 1;
while (r < d[i].r) r++, cnt[a[r]]++, ans += 2*cnt[a[r]] - 1;
while (l < d[i].l) cnt[a[l]]--, ans -= 2*cnt[a[l]] + 1, l++;
while (r > d[i].r) cnt[a[r]]--, ans -= 2*cnt[a[r]] + 1, r--;
anss[d[i].id] = ans;
}
for(int i = 1; i <= m; ++i) printf("%lld\n", anss[i]);
return 0;
}
原文地址:https://www.cnblogs.com/Ycrpro/p/8761440.html
时间: 2024-11-03 22:03:52