AcWing 251 小Z的袜子 (莫队)
题目链接:https://www.acwing.com/problem/content/description/253/
莫队算法:对询问分块
先将询问按左端点递增排序,然后将询问分成 \(\sqrt{n}\) 块,块内再将询问按右端点递增排序
块内相邻左端点变化不会超过 \(\sqrt{n}\) ,而右端点变化之和为 \(n\)
所以复杂度是 \(O(n\sqrt{n})\)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 100010;
int n, m, t, len, tot;
int c[maxn], b[maxn], L[maxn], R[maxn], pos[maxn], cnt[maxn];
ll ans;
struct Q{
int id;
int L, R;
ll ans;
}q[maxn];
bool cmp_L(Q a, Q b){
if(a.L == b.L) return a.R < b.R;
return a.L < b.L;
}
bool cmp_R(Q a, Q b){
if(a.R == b.R) return a.L < b.L;
return a.R < b.R;
}
bool cmp_id(Q a, Q b){
return a.id < b.id;
}
void add(int i){
ans -= 1ll * cnt[c[i]] * (cnt[c[i]] - 1) / 2;
++cnt[c[i]];
ans += 1ll * cnt[c[i]] * (cnt[c[i]] - 1) / 2;
}
void del(int i){
ans -= 1ll * cnt[c[i]] * (cnt[c[i]] - 1) / 2;
--cnt[c[i]];
ans += 1ll * cnt[c[i]] * (cnt[c[i]] - 1) / 2;
}
ll gcd(ll a, ll b){ return b ? gcd(b, a % b) : a; }
ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; }
int main(){
ans = 0;
n = read(), m = read();
t = sqrt(n); len = m / t;
//分块预处理
for(int i = 1; i <= t; ++i){
L[i] = (i - 1) * len + 1;
R[i] = i * len;
}
for(int i = 1; i <= t; ++i){
for(int j = L[i] ; j <= R[i]; ++j){
pos[j] = i;
}
}
if(R[t] < m){ ++t; L[t] = R[t - 1] + 1; R[t] = m; }
//
// 离散化
for(int i = 1; i <= n ; ++i) c[i] = read(), b[i] = c[i];
sort(b + 1, b + 1 + n);
tot = unique(b + 1, b + 1 + n) - b - 1;
for(int i = 1 ; i <= n; ++i) c[i] = lower_bound(b + 1, b + 1 + tot, c[i]) - b;
for(int i = 1; i <= m; ++i){
q[i].L = read(), q[i].R = read();
q[i].id = i;
}
sort(q + 1, q + 1 + m, cmp_L);
int cl, cr;
for(int i = 1; i <= t; ++i){
memset(cnt, 0 ,sizeof(cnt));
ans = 0;
sort(q + L[i], q + 1 + R[i], cmp_R);
int ql = q[L[i]].L, qr = q[L[i]].R; cl = ql, cr = qr;
for(int j = ql; j <= qr; ++j) ++cnt[c[j]];
for(int j = 1; j <= tot; ++j) ans += cnt[j] * (cnt[j] - 1) / 2;
q[L[i]].ans = ans;
for(int j = L[i] + 1; j <= R[i]; ++j){
ql = q[j].L, qr = q[j].R;
while(cl > ql) add(cl - 1), --cl;
while(cl < ql) del(cl), ++cl;
while(cr < qr) add(cr + 1), ++cr;
q[j].ans = ans;
}
}
sort(q + 1, q + 1 + m, cmp_id);
for(int i = 1; i <= m; ++i){
ll A = q[i].ans, B = q[i].R - q[i].L + 1;
B = 1ll * B * (B - 1) / 2;
ll gc = gcd(A, B);
if(A == 0) printf("0/1\n");
else printf("%lld/%lld\n", A / gc, B / gc);
}
return 0;
}