BZOJ-2038 小Z的袜子
离线做法:莫队。
离线处理询问,对左端点进行分块,在同一块内的右端点则从小到大排序。
接着对于上一个询问[l,r]和当今询问[l',r']不断调整区间范围。
怎么调整呢?对于[l,r]所含的信息,我们可以花O(1)的时间调整成[l,r+1],[l,r-1],[l+1,r],[l-1,r]。
最正宗的莫队貌似是点集曼哈顿距离加最小生成树,挺麻烦的。。。于是写成分块莫队。
#include <cstdlib> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <fstream> #include <iostream> #include <queue> #define rep(i, l, r) for(int i = l; i <= r; i++) #define down(i, l, r) for(int i = l; i >= r; i--) #define maxn 56789 #define MAX 1<<30 #define ll long long using namespace std; int n, m, c[maxn], pos[maxn]; ll ans, k, s[maxn]; struct node {int l, r, id; ll a, b;} q[maxn]; bool cmp(node a, node b) { if (pos[a.l] == pos[b.l]) return a.r < b.r; return a.l < b.l; } bool cmp_id(node a, node b) { return a.id < b.id; } ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);} ll sqr(ll x){return x*x;} void update(int p, int x) { ans -= sqr(s[c[p]]); s[c[p]] += x; ans += sqr(s[c[p]]); } int main() { scanf("%d%d", &n, &m); rep(i, 1, n) scanf("%d", &c[i]); int block = int(sqrt(n)); rep(i, 1, n) pos[i] = (i-1)/block+1; rep(i, 1, m) scanf("%d%d", &q[i].l, &q[i].r); rep(i, 1, m) q[i].id = i; sort(q+1, q+1+m, cmp); int l = 1, r = 0; rep(i, 1, m) { while (r < q[i].r) update(++r, 1); while (r > q[i].r) update(r--, -1); while (l < q[i].l) update(l++, -1); while (l > q[i].l) update(--l, 1); if (q[i].l == q[i].r) q[i].a = 0, q[i].b = 1; else { q[i].a = ans-(q[i].r-q[i].l+1); q[i].b = (ll)(q[i].r-q[i].l+1)*(q[i].r-q[i].l); k = gcd(q[i].a, q[i].b); q[i].a /= k; q[i].b /= k; } } sort(q+1, q+1+m, cmp_id); rep(i, 1, m) printf("%lld/%lld\n", q[i].a, q[i].b); return 0; }