P1494 [国家集训队] 小 Z 的袜子
莫队模板.
不难得出,对于一个长度为n的区间,所求即为:(区间内每种颜色数目的平方和 - n) / (n(n-1))
关键在于维护区间内每种颜色数目的平方和,
把区间分成O(√n)块,每个位置记录所属块pos,
对问题按第一关键字为pos, 第二关键字为r排序,从而保证时间复杂度为O(n√n)
每个问题答案在上一个问题基础上增减,代码如下:
#include <cstdio>
#include <cctype>
#include <cmath>
#include <algorithm>
using namespace std;
#define ll long long
inline int read(){
int res = 0, pdf = 0; char ch = getchar();
while(!isdigit(ch)) pdf = ch == '-', ch = getchar();
while(isdigit(ch)) res = (res<<3) + (res<<1) + (ch^48), ch = getchar();
return pdf ? -res : res;
}
inline void Print(ll x) {
if (x < 0) x = -x, putchar('-');
if (x < 10) putchar(x + '0');
else {
Print(x / 10);
putchar(x % 10 + '0');
}
}
inline ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
const int N = 5e5 + 10;
int n, m, block, col[N], pos[N]; ll num[N];
ll ans;
struct Ask {
int l, r, id;
ll fz, fm;
};
Ask a[N];
void update(int p, ll add) {
ans -= num[col[p]] * num[col[p]];
num[col[p]] += add;
ans += num[col[p]] * num[col[p]];
}
void Solve() {
ll l = 1, r = 0;
for (int i = 1; i <= m; ++i) {
while (a[i].l < l) { update(--l, 1); }
while (a[i].l > l) { update(l++, -1); }
while (a[i].r < r) { update(r--, -1); }
while (a[i].r > r) { update(++r, 1); }
if (a[i].l == a[i].r) {
a[i].fz = 0; a[i].fm = 1;
continue;
}
a[i].fz = ans - (r - l + 1);
a[i].fm = (r - l + 1) * (r - l);
ll GCD = gcd(a[i].fz, a[i].fm);
a[i].fz /= GCD; a[i].fm /= GCD;
}
}
bool cmp1(Ask x, Ask y) {
if (pos[x.l] == pos[y.l]) return x.r < y.r;
else return pos[x.l] < pos[y.l];
}
bool cmp2(Ask x, Ask y) {
return x.id < y.id;
}
int main() {
n = read(); m = read();
block = sqrt(n);
for (int i = 1; i <= n; ++i) {
col[i] = read();
pos[i] = (i - 1) / block + 1;
}
for (int i = 1; i <= m; ++i) {
a[i].l = read(); a[i].r = read();
a[i].id = i;
}
sort(a + 1, a + m + 1, cmp1);
Solve();
sort(a + 1, a + m + 1, cmp2);
for (int i = 1; i <= m; ++i) {
Print(a[i].fz); putchar('/'); Print(a[i].fm); putchar('\n');
}
return 0;
}
补充:
1.序列长度为
n
n
n, 询问次数为
m
m
m, 块长为
S
S
S, 则时间复杂度为
O
(
n
2
S
+
m
S
)
O(\frac {n^2} {S} + mS)
O(Sn2+mS),
m
S
mS
mS是因为移动可能跨越块,当
S
=
n
m
S=\frac {n} {\sqrt{m}}
S=mn时时间复杂度最优,为
O
(
n
m
)
O(n\sqrt{m})
O(nm).
2.奇偶化排序即对于属于奇数块的询问,r 按从小到大排序,对于属于偶数块的排序,r 从大到小排序,这样我们的 r 指针在处理完这个奇数块的问题后,将在返回的途中处理偶数块的问题,再向 n 移动处理下一个奇数块的问题,优化了 r 指针的移动次数,一般情况下,这种优化能让程序快 30% 左右。
3.小细节:如果使用 sort 比较两个函数,不能出现
a
>
b
a>b
a>b 和
a
<
b
a<b
a<b 同时为真的情况,否则会运行错误。