块内移动的距离显然是不超过块的大小的,这里定义块的大小为 n,一共 m 此询问,那么时间复杂度就是 O(mn)
在两个块之间移动,考虑最远距离 2n,总时间复杂度也是 O(mn) 的
再考虑右端点,因为对于左端点再同一个块的询问,右端点是单调递增的,那么就是 O(n),
一共有 n 个块,总时间复杂度就是 nn
综上所述,时间复杂度可以认为是 O(nn) 的,有时候如果TLE了可以尝试修改块的大小
(这里没有画图,可以自己结合图像方便理解)
代码
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #define get(x) ((x) / len) using namespace std; const int N = 1e6 + 5;
int n, len, cnt[N], w[N], ans[N]; struct query { int id, l, r; bool operator < (const query t) const { int a = get(l), b = get(t.l); if (a != b) return a < b; else { if (a & 1) return r < t.r; else return r > t.r; } } } q[N];
inline int read() { int x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch>='0'&&ch<='9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); } return x * f; }
int main() { int n; n = read(); for (int i = 1; i <= n; i++) w[i] = read(); int m; m = read(); len = 1720; 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); int res = 0; for (int k = 1, i = 0, j = 1; k <= m; k++) { int id = q[k].id, l = q[k].l, r = q[k].r; while (i < r) { int t = w[++i]; if (!cnt[t]) res++; cnt[t]++; } while (i > r) { int t = w[i--]; cnt[t]--; if (!cnt[t]) res--; } while (j > l) { int t = w[--j]; if (!cnt[t]) res++; cnt[t]++; } while (j < l) { int t = w[j++]; cnt[t]--; if (!cnt[t]) res--; } ans[id] = res; } for (int i = 1; i <= m; i++) printf("%d\n", ans[i]); return 0; }
最后考虑右端点,块内移动与 l 类似,考虑块间,对于左端点的每一个块, r 都会移动 n 步,所以实际按复杂度就是 O(am+an2)
但是 max(O(am+n),O(ma2n2),O(am+an2)) 该怎么取呢?
首先,我们肯定要让 a≥n,不然 O(ma2n2) 就是 n2 级别的了
所以忽略掉较小项就是 max(O(am),O(ma2n2))
要让这个最小就是 am=ma2n2⇒a=n32
时间复杂度就是 O(n35),卡一卡能过
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #define p first #define val second #define get(x) ((x) / len) using namespace std; const int N = 2e5 + 5; typedef pair<int, int> PII;
PII my[N]; int w[N], cnt, tot, len, tim[N * 10], ans[N]; struct query { int id, l, r, t; bool operator < (const query a) const { int al = get(l), ar = get(r); int bl = get(a.l), br = get(a.r); if (al != bl) return al < bl; if (ar != br) return ar < br; return t < a.t; } } q[N];
int main() { int n, m; scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d", &w[i]); for (int i = 1; i <= m; i++) { char opt; int l, r; scanf(" %c", &opt); if (opt == 'Q') { scanf("%d%d", &l, &r); cnt++; q[cnt] = query({cnt, l, r, tot}); } else { scanf("%d%d", &l, &r); my[++tot] = make_pair(l, r); } } len = pow(n, 2.0 / 3); sort(q + 1, q + 1 + cnt); for (int k = 1, i = 0, j = 1, t = 0, res = 0; k <= cnt; k++) { int id = q[k].id, l = q[k].l, r = q[k].r, to = q[k].t; while (i < r) { int tp = w[++i]; if (!tim[tp]) res++; tim[tp]++; } while (i > r) { int tp = w[i--]; tim[tp]--; if (!tim[tp]) res--; } while (j > l) { int tp = w[--j]; if (!tim[tp]) res++; tim[tp]++; } while (j < l) { int tp = w[j++]; tim[tp]--; if (!tim[tp]) res--; } while (t < to) { t++; if (my[t].p >= j && my[t].p <= i) { int pi = my[t].p; tim[w[pi]]--; if (!tim[w[pi]]) res--; if(!tim[my[t].val]) res++; tim[my[t].val]++; } swap(my[t].val, w[my[t].p]); } while (t > to) { if (my[t].p >= j && my[t].p <= i) { int pi = my[t].p; tim[w[pi]]--; if (!tim[w[pi]]) res--; if(!tim[my[t].val]) res++; tim[my[t].val]++; } swap(my[t].val, w[my[t].p]); t--; } ans[id] = res; } for (int i = 1; i <= cnt; i++) printf("%d\n", ans[i]); return 0; }
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #define pl first #define pr second #define INF 0x3f3f3f3f #define get(x) ((x) / len) using namespace std; const int N = 2e5 + 5; typedef long long ll; typedef pair<int, int> PII;
PII bp[N], pi[N]; int len, w[N], ans[N], temp[N], val[N], cnt; struct query { int id, l, r; bool operator < (const query a) const { int x = get(l), y = get(a.l); if (x != y) return x < y; else return r < a.r; } } q[N];
inline int find(int x) { int l = 1, r = cnt; while (l <= r) { int mid = (l + r) >> 1; if (temp[mid] < x) l = mid + 1; else if (temp[mid] == x) return mid; else r = mid - 1; } }
inline int read() { int x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); } return x * f; }
int main() { // freopen("1.in", "r", stdin); int n = read(); for (int i = 1; i <= n; i++) val[i] = w[i] = read(); sort(w + 1, w + 1 + n); for (int i = 1; i <= n; i++) if (temp[cnt] != w[i]) temp[++cnt] = w[i]; for (int i = 1; i <= n; i++) w[i] = find(val[i]); int m = read(); for (int i = 1; i <= m; i++) q[i].l = read(), q[i].r = read(), q[i].id = i; len = sqrt(n); sort(q + 1, q + 1 + m); for (int x = 1; x <= m;) { int y = x; while (y <= m && get(q[x].l) == get(q[y].l)) y++; int R = get(q[x].l) * len + len; while (x < y && q[x].r <= R) { int res = 0; int id = q[x].id, l = q[x].l, r = q[x].r; for (int k = l; k <= r; k++) bp[w[k]] = pi[w[k]]; for (int k = l; k <= r; k++) { bp[w[k]].pr = k; if (!bp[w[k]].pl) bp[w[k]].pl = k; res = max(res, k - bp[w[k]].pl); } ans[id] = res; x++; } int res = 0, i = R, j = R + 1; while (x < y) { int id = q[x].id, l = q[x].l, r = q[x].r; while (i < r) { i++, pi[w[i]].pr = i; if (!pi[w[i]].pl) pi[w[i]].pl = i; res = max(res, i - pi[w[i]].pl); } int backup = res; while (j > l) { if (!pi[w[--j]].pr) pi[w[j]].pr = j; res = max(res, pi[w[j]].pr - j); } while (j <= R) { if (pi[w[j]].pr == j) pi[w[j]].pr = 0; j++; } ans[id] = res; res = backup; x++; } for (int i = 1; i <= cnt; i++) pi[i].pl = pi[i].pr = 0; } for (int i = 1; i <= m; i++) printf("%d\n", ans[i]); return 0; }
记录每个点在欧拉序中第一个出现的位置 fir 与第二个出现的位置 sec,对于当前的 u,v 之间的路径 若 u 为 v 的父亲,就是直接在欧拉序 [firu,firv] 中只出现了一次的数
若 u, v 在两颗子树内,就找 [secu,firv] 中只出现了一次的数,然后再加上 lca(u,v) 的值.
这里想一想就能明白,统计次数和普通莫队类似可以参考代码
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #define get(x) ((x) / len) using namespace std; const int N = 1e5 + 5;
bool st[N]; int fir[N], sec[N], seq[N], top; int head[N], ver[N], net[N], idx; int w[N], temp[N], val[N], cnt[N], len; int depth[N], fa[N][21], ans[N], tot; struct query { int id, l, r, p; bool operator < (const query a) const { int x = get(l), y = get(a.l); if (x != y) return x < y; if (x & 1) return r < a.r; return r > a.r; } } q[N];
inline int read() { int x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (x == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); } return x * f; }
inline void add(int a, int b) { net[++idx] = head[a], ver[idx] = b, head[a] = idx; net[++idx] = head[b], ver[idx] = a, head[b] = idx; }
void dfs(int u, int f) { seq[++top] = u, fir[u] = top; depth[u] = depth[f] + 1, fa[u][0] = f; for (int i = 1; i <= 17; i++) fa[u][i] = fa[fa[u][i - 1]][i - 1]; for (int i = head[u]; i; i = net[i]) { int v = ver[i]; if (v == f) continue; dfs(v, u); } seq[++top] = u, sec[u] = top; }
inline int lca(int u, int v) { if (depth[u] < depth[v]) swap(u, v); for (int i = 17; i >= 0; i--) if (depth[fa[u][i]] >= depth[v]) u = fa[u][i]; if (u == v) return u; for (int i = 17; i >= 0; i--) if (fa[u][i] != fa[v][i]) u = fa[u][i], v = fa[v][i]; return fa[u][0]; }
inline void modify(int x, int& res) { st[x] ^= 1; if (st[x] == 0) { cnt[w[x]]--; if (!cnt[w[x]]) res--; } else { if (!cnt[w[x]]) res++; cnt[w[x]]++; } }
inline int find(int x) { int l = 1, r = tot; while (l <= r) { int mid = (l + 1) >> 1; if (temp[mid] < x) l = mid + 1; else if (temp[mid] == x) return mid; else r = mid - 1; } }
int main() { int n, m; n = read(), m = read(); for (int i = 1; i <= n; i++) val[i] = w[i] = read(); sort(w + 1, w + 1 + n); for (int i = 1; i <= n; i++) if (temp[tot] != w[i]) temp[++tot] = w[i]; for (int i = 1; i <= n; i++) w[i] = find(val[i]); for (int i = 1; i < n; i++) add(read(), read()); dfs(1, 0); for (int i = 1; i <= m; i++) { int u = read(), v = read(); if (fir[u] > fir[v]) swap(u, v); int p = lca(u, v); if (u == p) q[i] = query({i, fir[u], fir[v], 0}); else q[i] = query({i, sec[u], fir[v], p}); } sort(q + 1, q + 1 + m); for (int k = 1, L = 1, R = 0, res = 0; k <= m; k++) { int id = q[k].id, l = q[k].l, r = q[k].r, p = q[k].p; while (R < r) modify(seq[++R], res); while (R > r) modify(seq[R--], res); while (L > l) modify(seq[--L], res); while (L < l) modify(seq[L++], res); if (p) add(seq[p], res); ans[id] = res; if (p) add(seq[p], res); } for (int i = 1; i <= m; i++) printf("%d\n", ans[i]); return 0; }