前言
题目是乱序的。因为太菜了不会卡常,所以很多题是九十几分。
UPD. 2022.09.19
改名为 DS 记录
UPD. 2022.04.27
发现很多以前写的懒得写题解,感觉又都不太会了,后面还是做一题写一题比较好。
发现刷 Ynoi 收益很小,大部分时间只是在卡常。
不过数据结构题写起来有意思,自己写起来乐在其中,可能刷 Ynoi 对于算法竞赛并没有什么用,更多只是自我满足罢了。
P5047 [Ynoi2019 模拟赛] Yuno loves sqrt technology II
给定序列 \(a_n\), \(m\) 次询问区间逆序对。
\(1 \leq n, m \leq 10^5, 1 \leq a_i \leq 10^9\)。
莫队二次离线。
求前缀比它小/大的数的个数,用 \(O(\sqrt n)\) 修改,\(O(1)\) 查询的搞一下就行。
#include <bits/stdc++.h>
constexpr int T(512), N(1e5 + 5), S(N / T + 1);
struct Qry {
int l, r, id;
bool operator<(const Qry &rhs) const {
return l / T == rhs.l / T ? l / T & 1 ? r < rhs.r : r > rhs.r : l < rhs.l;
}
};
int n, m;
int a[N], s[S];
void clear() {
(a, 0, sizeof a);
memset(s, 0, sizeof s);
memset}
void add(int p) {
int t = p / T;
for (int i = t * T; i <= p; i++) a[i]++;
for (int i = 0; i < t; i++) s[i]++;
}
int ask(int p) {
= std::max(p, 0);
p return a[p] + s[p / T];
}
int main() {
std::ios::sync_with_stdio(0);
std::cin.tie(0);
std::cin >> n >> m;
std::vector<int> a(n);
for (int &x : a) std::cin >> x;
auto b = a;
std::sort(b.begin(), b.end());
.resize(std::unique(b.begin(), b.end()) - b.begin());
bfor (int &x : a) x = std::lower_bound(b.begin(), b.end(), x) - b.begin();
std::vector<long long> c(n + 1), d(n + 1);
for (int i = 0; i < n; i++) {
(a[i]);
add[i + 1] = c[i] + ask(a[i + 1] + 1);
c[i + 1] = d[i] + i + 1 - ask(a[i]);
d}
std::vector<Qry> q(m);
for (int i = 0; i < m; i++) {
std::cin >> q[i].l >> q[i].r;
[i].l--, q[i].r--;
q[i].id = i;
q}
std::sort(q.begin(), q.end());
std::vector<long long> ans(m), out(m);
std::vector<std::vector<std::array<int, 3>>> g(n), h(n);
for (int i = 0, x = 1, y = 0; i < m; i++) {
if (y != q[i].r) {
[i] += c[q[i].r] - c[y];
ansif (x) y < q[i].r ? g[x - 1].push_back({y + 1, q[i].r, -i - 1}) : g[x - 1].push_back({q[i].r + 1, y, i});
= q[i].r;
y }
if (x != q[i].l) {
[i] += d[q[i].l] - d[x];
ans< q[i].l ? h[y].push_back({x, q[i].l - 1, -i - 1}) : h[y].push_back({q[i].l, x - 1, i});
x = q[i].l;
x }
}
();
clearfor (int i = 0; i < n; i++) {
(a[i]);
addfor (auto [l, r, id] : g[i]) {
int c = id < 0 ? id = -id - 1, -1 : 1;
for (int j = l; j <= r; j++) {
[id] += c * ask(a[j] + 1);
ans}
}
for (auto [l, r, id] : h[i]) {
int c = id < 0 ? id = -id - 1, -1 : 1;
for (int j = l; j <= r; j++) {
[id] += c * (i + 1 - ask(a[j]));
ans}
}
}
for (int i = 1; i < m; i++) ans[i] += ans[i - 1];
for (int i = 0; i < m; i++) out[q[i].id] = ans[i];
for (auto x : out) std::cout << x << "\n";
return 0;
}
P5046 [Ynoi2019 模拟赛] Yuno loves sqrt technology I
给定 \(1\dots n\) 的 排列 \(a_n\), \(m\) 次询问区间逆序对。
\(1 \leq n, m \leq 10^5\)。
强制在线。
序列分块,块内预处理前缀后缀逆序对,每块预处理该块和原序列前缀的逆序对。
块内用两个前缀相减,再归并计算两个前缀之间的贡献。
整块直接用预处理的计算,然后用归并计算两个散块之间的贡献。
\(O(n \sqrt n)\)。
// Author: HolyK
// Created: Tue Sep 14 14:01:15 2021
#include <bits/stdc++.h>
template <class T, class U>
inline bool smin(T &x, const U &y) {
return y < x ? x = y, 1 : 0;
}
template <class T, class U>
inline bool smax(T &x, const U &y) {
return x < y ? x = y, 1 : 0;
}
using LL = long long;
using PII = std::pair<int, int>;
inline char gc() {
static constexpr int BufferSize = 1 << 22 | 5;
static char buf[BufferSize], *p, *q;
static std::streambuf *i = std::cin.rdbuf();
return p == q ? p = buf, q = p + i->sgetn(p, BufferSize), p == q ? EOF : *p++ : *p++;
}
struct Reader {
template <class T>
&operator>>(T &w) {
Reader char c, p = 0;
for (; !std::isdigit(c = gc());) if (c == '-') p = 1;
for (w = c & 15; std::isdigit(c = gc()); w = w * 10 + (c & 15)) ;
if (p) w = -w;
return *this;
}
} cin;
constexpr int N(1e5 + 5), T(512);
(PII *a, PII *b, int l1, int r1, PII *c, PII *d, int l2, int r2) {
LL cal= 0;
LL res for (int now = 0; a < b; a++) {
if (a->second < l1 || a->second >= r1) continue;
for (; c < d && *c < *a; c++) {
if (c->second < l2 || c->second >= r2) continue;
++;
now}
+= now;
res }
return res;
}
int n, m, a[N];
[N], bb[N];
PII bint c[N];
void add(int p, int x) {
for (; p <= n; p += p & -p) c[p] += x;
}
int ask(int p) {
int r = 0;
for (; p; p -= p & -p) r += c[p];
return r;
}
constexpr int S((N - 1 + T) / T);
[N], suf[N], cnt[S][N];
LL pre
int main() {
// freopen("t.in", "r", stdin);
// freopen(".out", "w", stdout);
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
>> n >> m;
cin
for (int i = 0; i < n; i++) {
>> a[i];
cin [i] = b[i] = {a[i], i};
bb}
std::sort(bb, bb + n);
for (int l = 0, o = 0; l < n; l += T, o++) {
int r = std::min(n, l + T);
std::sort(b + l, b + r);
for (int i = 0, j = l; i < n || j < r;) {
if (j == r || i < n && bb[i] < b[j]) {
[o][bb[i].second] += j - l;
cnt++;
i} else {
++;
j}
}
for (int i = 1; i < n; i++) cnt[o][i] += cnt[o][i - 1];
for (int i = l; i < r; i++) {
[i] = i - l - ask(a[i]);
pre(a[i], 1);
add}
for (int i = l; i < r; i++) {
(a[i], -1);
add}
for (int i = r - 1; i >= l; i--) {
[i] = ask(a[i]);
suf(a[i], 1);
add}
for (int i = l; i < r; i++) {
(a[i], -1);
add}
for (int i = l + 1; i < r; i++) pre[i] += pre[i - 1];
for (int i = r - 2; i >= l; i--) suf[i] += suf[i + 1];
}
= 0;
LL ans while (m--) {
, r;
LL l>> l >> r;
cin ^= ans, r ^= ans;
l --, r--;
lint bl = l / T, br = r / T;
if (bl == br) {
= pre[r] - (l == bl * T ? 0 : pre[l - 1]);
ans int x = bl * T, y = std::min(n, x + T);
-= cal(b + x, b + y, x, l, b + x, b + y, l, r + 1);
ans } else {
= suf[l] + pre[r];
ans += std::max(0, (br - bl - 1) * T) * (r - br * T + 1LL);
ans
for (int i = bl + 1; i < br; i++) {
+= suf[i * T];
ans += cnt[i][i * T - 1];
ans if (l) ans -= cnt[i][l - 1];
-= cnt[i][r] - cnt[i][br * T - 1];
ans }
// std::cerr << ans << "\n";
+= cal(b + bl * T, b + bl * T + T, l, bl * T + T,
ans + br * T, b + std::min(n, br * T + T), br * T, r + 1);
b }
std::cout << ans << "\n";
// break;
}
return 0;
}
P5048 [Ynoi2019 模拟赛] Yuno loves sqrt technology III
给定序列 \(a_n\), \(m\) 次询问区间众数出现的次数。
\(1 \leq n, m \leq 10^5, 1 \leq a_i \leq 10^9\)。
强制在线。
区间众数要么是中间的整块的众数,要么是两边散块的。
预处理整块两两之间区间的众数个数,然后用 vector 存每个数出现的位置。
对于散块每个数,直接看它的位置 +ans 之后还在不在询问区间内,在的话就暴力拓展。拓展次数是 \(O(\sqrt n)\) 级别。
复杂度 \(O(n \sqrt n)\)。
// Author: HolyK
// Created: Tue Sep 14 17:41:10 2021
#include <bits/stdc++.h>
template <class T, class U>
inline bool smin(T &x, const U &y) {
return y < x ? x = y, 1 : 0;
}
template <class T, class U>
inline bool smax(T &x, const U &y) {
return x < y ? x = y, 1 : 0;
}
using LL = long long;
using PII = std::pair<int, int>;
constexpr int N(5e5 + 5), T(512), S((N - 1 + T) / T);
int n, m, a[N], b[N], c[N], pos[N], cnt[S][S];
struct LazyVector {
std::vector<int> a, b;
int c;
(int n) : a(n), b(n), c(0) {}
LazyVectorvoid clear() { c++; }
int &operator[](const int &i) {
if (b[i] != c) b[i] = c, a[i] = 0;
return a[i];
}
};
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n >> m;
for (int i = 0; i < n; i++) {
std::cin >> a[i];
[i] = a[i];
b}
std::sort(b, b + n);
int t = std::unique(b, b + n) - b;
for (int i = 0; i < n; i++) {
[i] = std::lower_bound(b, b + t, a[i]) - b;
a[a[i]]++;
c}
for (int i = 1; i < t; i++) c[i] += c[i - 1];
for (int i = n - 1; i >= 0; i--) {
[--c[a[i]]] = i;
b[i] = c[a[i]];
pos}
(t);
LazyVector vfor (int i = 0; i < n; i += T) {
int max = 0;
.clear();
vfor (int j = i + T; j < n; j += T) {
for (int k = j - T; k < j; k++)
(max, ++v[a[k]]);
smax[i / T][j / T] = max;
cnt}
}
int ans = 0;
while (m--) {
int l, r;
std::cin >> l >> r;
^= ans, r ^= ans;
l --, r--;
lint bl = l / T, br = r / T;
if (bl == br) {
= 0;
ans .clear();
vfor (int i = l; i <= r; i++) {
(ans, ++v[a[i]]);
smax}
} else {
= cnt[bl + 1][br];
ans int y = bl * T + T, x = br * T;
for (int i = l; i < y; i++) {
while (pos[i] + ans < n && b[pos[i] + ans] <= r && a[b[pos[i] + ans]] == a[i]) ans++;
}
for (int i = x; i <= r; i++) {
while (pos[i] - ans >= 0 && b[pos[i] - ans] >= l && a[b[pos[i] - ans]] == a[i]) ans++;
}
}
std::cout << ans << "\n";
}
return 0;
}
P7447 [Ynoi2007] rgxsxrs
给定一个序列 \(a_n\),\(m\) 次操作:
区间 \([l, r]\) 大于 \(x\) 的数减 \(x\);求区间和、最大最小值。
\(n, m \leq 5 \times 10^5, 1 \leq a_i, x \leq 10^9\)
空间限制 64MB,强制在线。
值域倍增分块,\([b^k, b^{k+1})\) 在一个块里,所以有 \(\log_b a\) 个块。每一块开一个线段树。
修改时,若 \(x < b^k\),则块内所有数都要减 \(x\),有些数被减后会掉落到更小的值域块中,掉落操作会发生 \(n \log_b a\) 次,每次需要 \(\log n\) 的时间定位和修改,均摊下来还是 \(O(n\log_b a \log n)\)。
若 \(b^k \leq x < b^{k+1}\),则块内有些数需要减 \(x\)。因为 \(x \ge b^k\),所以每个数最多减 \(b\) 次就会掉落,所以每个数最多会被减 \(b\log_b a\) 次,最坏情况下每次需要 \(\log n\) 时间定位,所以是 \(O(nb\log_b a \log n)\)。
若 \(x \ge b^{k+1}\) 则什么都不用做。
设 \(n, m\) 同阶,时间复杂度是 \(O(nb \log n \log_b a)\),空间复杂度是 \(O(n \log_b a)\),取 \(b = 2\) 时间最优,但是空间炸了。
考虑先将原序列分块,块大小 \(B = \log_b a\),然后线段树节点数变为 \(O(n / B)\),这样线段树定位和修改的复杂度变成了 \(O(\log n + B)\),仅仅增加了一点常数。
在实现时,要根据实际情况选取 \(b, B\) 的大小,为了 cache 友好,不写 \(\log_b a\) 棵线段树,而是每个线段树维护 \(O(\log_b a)\) 的信息。
// Author: HolyK
// Created: Fri Aug 27 21:50:45 2021
#include <bits/stdc++.h>
template <class T, class U>
inline bool smin(T &x, const U &y) {
return y < x ? x = y, 1 : 0;
}
template <class T, class U>
inline bool smax(T &x, const U &y) {
return x < y ? x = y, 1 : 0;
}
inline char gc() {
static constexpr int BufferSize = 1 << 22 | 5;
static char buf[BufferSize], *p, *q;
static std::streambuf *i = std::cin.rdbuf();
return p == q ? p = buf, q = p + i->sgetn(p, BufferSize), p == q ? EOF : *p++ : *p++;
}
struct Reader {
template <class T>
&operator>>(T &w) {
Reader char c, p = 0;
for (; !std::isdigit(c = gc());) if (c == '-') p = 1;
for (w = c & 15; std::isdigit(c = gc()); w = w * 10 + (c & 15)) ;
if (p) w = -w;
return *this;
}
} cin;
using LL = long long;
using PII = std::pair<int, int>;
using u64 = uint64_t;
using u32 = uint32_t;
constexpr int N(5e5), T(8), P(32 / T), S(1 << P), B(32), L((N + B - 1) / B);
int n, m, k;
[N];
u32 a[T + 1];
u64 pwstruct Info {
, max, siz;
u32 min;
u64 sum&operator+=(const Info &r) {
Info (min, r.min);
smin(max, r.max);
smax+= r.siz;
siz += r.sum;
sum return *this;
}
void tag(const u32 &x) {
-= x, max -= x, sum -= u64(siz) * x;
min }
void ins(const u32 &x) {
(min, x), smax(max, x), siz++, sum += x;
smin}
} t[L * 4][T];
[L * 4][T];
u32 tag(u32 x) { return std::__lg(x) / P; }
u32 posconstexpr Info Null = {~0U, 0U, 0U, 0ULL};
inline Info operator+(const Info &a, const Info &b) { return {std::min(a.min, b.min), std::max(a.max, b.max), a.siz + b.siz, a.sum + b.sum}; }
#define ls o << 1
#define rs o << 1 | 1
#define APPLY() for (int _ = 0; _ < T; _ += 4) F(_), F(_ + 1), F(_ + 2), F(_ + 3)
void pushup(int o) {
#define F(i) t[o][i] = t[ls][i] + t[rs][i]
();
APPLY#undef F
}
void add(int o, int i, u32 z) {
if (t[o][i].siz) {
[o][i].tag(z);
t[o][i] += z;
tag}
}
void pushdown(int o) {
#define F(i) (tag[o][i] ? (add(ls, i, tag[o][i]), add(rs, i, tag[o][i]), tag[o][i] = 0) : 1)
();
APPLY#undef F
}
, min;
u32 max;
u64 sumvoid ask(int o, int l, int r, int x, int y) {
if (r - l == 1) {
for (int i = l * B; i < r * B && i < n; i++) {
[i] -= tag[o][pos(a[i])];
a}
(tag[o], 0, sizeof tag[o]);
memsetfor (int i = x; i < y; i++) {
(max, a[i]);
smax(min, a[i]);
smin+= a[i];
sum }
return;
}
if (x <= l * B && r * B <= y) {
#define F(i) (smax(max, t[o][i].max), smin(min, t[o][i].min), sum += t[o][i].sum)
();
APPLY#undef F
// std::cerr << "range-ask: " << l << " " << r << " " << sum - pre << " ";
return;
}
(o);
pushdownint m = l + r >> 1;
if (y <= m * B) {
(ls, l, m, x, y);
ask} else if (x >= m * B) {
(rs, m, r, x, y);
ask} else {
(ls, l, m, x, m * B);
ask(rs, m, r, m * B, y);
ask}
}
void update(int o, int l, int r, int x, int y, u32 z, int bitl = 0, int bitr = T - 1) {
while (bitl <= bitr && t[o][bitl].max <= z) bitl++;
while (bitl <= bitr && t[o][bitr].max <= z) bitr--;
if (bitl > bitr) return;
if (r - l == 1) {
for (int i = l * B; i < r * B && i < n; i++) {
[i] -= tag[o][pos(a[i])];
a}
(tag[o], 0, sizeof tag[o]);
memset// std::cerr << "update-single: ";
for (int i = x; i < y; i++) {
if (a[i] > z && a[i] < pw[bitr + 1]) {
// std::cerr << i << " ";
[i] -= z;
a}
}
// std::cerr << "\n";
std::fill(t[o], t[o] + T, Null);
for (int i = l * B; i < r * B && i < n; i++) {
[o][pos(a[i])].ins(a[i]);
t}
return;
}
if (x <= l * B && r * B <= y) {
for (; bitl <= bitr; bitr--) {
const int &i = bitr;
if (t[o][i].min > z && t[o][i].min - z >= pw[i]) {
(o, i, z);
addcontinue;
}
break;
}
if (bitl > bitr) {
return;
}
}
(o);
pushdownint m = l + r >> 1;
if (y <= m * B) {
(ls, l, m, x, y, z, bitl, bitr);
update} else if (x >= m * B) {
(rs, m, r, x, y, z, bitl, bitr);
update} else {
(ls, l, m, x, m * B, z, bitl, bitr);
update(rs, m, r, m * B, y, z, bitl, bitr);
update}
(o);
pushup
}
void build(int o, int l, int r) {
if (r - l == 1) {
std::fill(t[o], t[o] + T, Null);
int x = l * B, y = std::min(r * B, n);
for (int i = x; i < y; i++) {
[o][pos(a[i])].ins(a[i]);
t}
return;
}
int m = l + r >> 1;
(ls, l, m), build(rs, m, r);
build(o);
pushup}
int main() {
// freopen("tt.in", "r", stdin);
// freopen("t.out", "w", stdout);
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
[0] = 1;
pwfor (int i = 1; i <= T; i++) pw[i] = pw[i - 1] * S;
>> n >> m;
cin for (int i = 0; i < n; i++) cin >> a[i];
= (n + B - 1) / B;
k std::cerr << "k = " << k << "\n";
(1, 0, k);
buildint ans = 0;
while (m--) {
int opt, l, r;
>> opt >> l >> r;
cin ^= ans, r ^= ans;
l assert(l <= r);
--;
lif (opt == 1) {
;
u32 x>> x;
cin ^= ans;
x (1, 0, k, l, r, x);
update} else {
= 0U, min = ~0U, sum = 0ULL;
max (1, 0, k, l, r);
ask= sum % (1 << 20);
ans std::cout << sum << " " << min << " " << max << "\n";
}
}
std::cerr << (double)clock() / CLOCKS_PER_SEC << "\n";
return 0;
}
P6578 [Ynoi2019] 魔法少女网站
给定序列 \(a_n\),\(m\) 次操作,单点修改 \(a_x = y\),查询区间 \([l, r]\) 有多少子区间的最大值 \(\leq x\)。
\(n, m \leq 3 \times 10^5, 1 \leq a_i \leq n\)。
将操作分块,块大小为 \(B\)。
对于每个操作块,被修改的位置只有 \(O(B)\) 个。没被修改的位置和询问按值从小到大排序,然后插入,用链表维护连续段。
每次询问,枚举前面的修改操作,暴力插入,询问完之后回滚。
由于是区间询问,还要考虑将序列分块,两边的散块暴力,完整的块要维护块内完整连续段的答案之和,以及左右两边不完整连续段的端点。
设 \(n, m\) 同阶,取 \(B = \sqrt n\),复杂度是 \(O(n \sqrt n)\)。
// Author: HolyK
// Created: Wed Jul 14 11:25:19 2021
#include <bits/stdc++.h>
#define dbg(a...) fprintf(stderr, a)
template <class T, class U>
inline bool smin(T &x, const U &y) {
return y < x ? x = y, 1 : 0;
}
template <class T, class U>
inline bool smax(T &x, const U &y) {
return x < y ? x = y, 1 : 0;
}
using LL = long long;
using PII = std::pair<int, int>;
inline char gc() {
static constexpr int BufferSize = 1 << 22 | 5;
static char buf[BufferSize], *p, *q;
static std::streambuf *i = std::cin.rdbuf();
return p == q ? p = buf, q = p + i->sgetn(p, BufferSize), p == q ? EOF : *p++ : *p++;
}
struct Reader {
template <class T>
&operator>>(T &w) {
Reader char c, p = 0;
for (; !std::isdigit(c = gc());) if (c == '-') p = 1;
for (w = c & 15; std::isdigit(c = gc()); w = w * 10 + (c & 15)) ;
if (p) w = -w;
return *this;
}
} cin;
// using std::cin;
template <class T>
struct Rec {
std::vector<std::pair<T*, T>> rec;
(int n = 0) {
Rec.reserve(n);
rec}
void clear() { rec.clear(); }
void operator()(T &x) {
.emplace_back(&x, x);
rec}
void recover() {
while (!rec.empty()) *rec.back().first = rec.back().second, rec.pop_back();
}
};
constexpr int N(3e5 + 5), T(1024), S(N / T + 1);
struct Qry {
int l, r, x, id;
bool operator<(const Qry &rhs) const {
return x < rhs.x || x == rhs.x && id < rhs.id;
}
} q[N];
int n, m, tot;
[N << 1];
PII aint raw[N], val[N], b[N], pos[N], tmp[N];
[N];
LL f[S], ans[N];
LL sumint lp[S], rp[S], cnt[S];
bool vis[N];
struct Info {
int l, r, x;
} rec[N];
int recs;
void ins(int l, int r, int z) {
const int bl = l / T, br = r / T;
if (bl == br) {
[bl] += z * f[r - l + 1];
sum} else if (z > 0) {
[bl] = l, lp[br] = r;
rp}
}
void add(int x) {
if (b[x]) return;
[x / T]++;
cnt[x] = 1;
bint l = x, r = x;
if (x && b[x - 1]) {
= pos[x - 1];
l (pos[x - 1], x - 1, -1);
ins}
if (x + 1 < n && b[x + 1]) {
= pos[x + 1];
r (x + 1, pos[x + 1], -1);
ins}
[l] = r, pos[r] = l;
pos(l, r, 1);
ins}
void add_rec(int x) {
if (b[x]) return;
[x / T]++, b[x] = 1;
cntint l = x, r = x;
if (x && b[x - 1]) {
= pos[x - 1];
l (pos[x - 1], x - 1, -1);
ins}
if (x + 1 < n && b[x + 1]) {
= pos[x + 1];
r (x + 1, pos[x + 1], -1);
ins}
[l] = r, pos[r] = l;
pos(l, r, 1);
ins[++recs] = {l, r, x};
rec}
void recover() {
// ri.recover(), rl.recover();
while (recs) {
auto &[l, r, x] = rec[recs--];
[x / T]--, b[x] = 0;
cnt(l, r, -1);
insif (l / T != r / T) rp[l / T] = lp[r / T] = -1;
if (l < x) ins(l, x - 1, 1), pos[l] = x - 1, pos[x - 1] = l;
if (x < r) ins(x + 1, r, 1), pos[r] = x + 1, pos[x + 1] = r;
}
}
(int l, int r) {
LL askconst int bl = l / T, br = r / T;
= 0;
LL ans if (bl == br) {
int now = 0;
for (int i = l; i <= r; i++) {
if (b[i]) {
++;
now} else {
+= f[now];
ans = 0;
now }
}
return ans + f[now];
}
int y = bl * T + T, x = br * T, now = 0;
for (int i = l; i < y; i++) {
if (b[i]) {
++;
now} else {
+= f[now];
ans = 0;
now }
}
for (int i = bl + 1; i < br; i++, y += T) {
if (T == cnt[i]) {
+= T;
now continue;
}
if (~lp[i]) {
+= lp[i] - y + 1;
now }
+= f[now] + sum[i];
ans = 0;
now if (~rp[i]) {
= y + T - rp[i];
now }
}
for (int i = x; i <= r; i++) {
if (b[i]) {
++;
now} else {
+= f[now];
ans = 0;
now }
}
return ans + f[now];
};
int main() {
[0] = 0;
ffor (int i = 1; i <= 3e5; i++) {
[i] = f[i - 1] + i;
f}
double sta = (double)clock() / CLOCKS_PER_SEC;
#ifndef ONLINE_JUDGE
("t.in", "r", stdin);
freopen("tt.out", "w", stdout);
freopen#endif
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
>> n >> m, tot = (n - 1) / T + 1;
cin
for (int i = 0; i < n; i++) {
>> raw[i];
cin [i] = {raw[i], i};
a}
int ta = n, tq = 0;
for (int i = 0; i < m; i++) {
int opt;
>> opt >> q[i].l >> q[i].r;
cin [i].l--;
q[i].id = i;
qif (opt == 2) {
>> q[i].x;
cin [i].r--;
q++;
tq} else {
[ta++] = {q[i].r, q[i].l};
a[i] = -1;
ans}
}
std::sort(a, a + ta);
for (int i = 0, r, d; i < m; i = r) {
for (; i < m && !q[i].x; i++) {
[q[i].l] = q[i].r;;
raw}
= std::min(m, i + 1324);
r // for (int k = step; r < m && k; r++) k -= !q[r].x;
std::sort(q + i, q + r);
for (d = i; d < r && !q[d].x; d++) ;
// assert(d - i <= step);
for (int j = 0; j < n; j++) {
[j] = -1;
pos[j] = 0;
b[j] = 0;
vis[j] = raw[j];
val}
for (int j = 0; j < tot; j++) {
[j] = 0;
sum[j] = 0;
cnt[j] = rp[j] = -1;
lp}
for (int j = i; j < d; j++) {
[q[j].l] = 1;
vis}
[0] = 0;
tmpfor (int j = 0; j < n; j++) if (vis[j]) tmp[++tmp[0]] = j;
for (int j = d, k = 0; j < r; j++) {
auto [ql, qr, x, id] = q[j];
for (; k < ta && a[k].first <= x; k++) {
if (val[a[k].second] != a[k].first || vis[a[k].second]) continue;
(a[k].second);
add}
for (int t = i; t < d; t++) {
if (q[t].id > id) continue;
[q[t].l] = q[t].r;
val}
for (int t = 1; t <= tmp[0]; t++) {
if (val[tmp[t]] <= x) add_rec(tmp[t]);
}
[id] = ask(ql, qr);
ansfor (int t = 1; t <= tmp[0]; t++) {
[tmp[t]] = raw[tmp[t]];
val}
();
recover}
for (int j = i; j < d; j++) {
[q[j].l] = q[j].r;
raw}
}
for (int i = 0; i < m; i++) if (~ans[i]) std::cout << ans[i] << "\n";
std::cerr << "time : " << (double)clock() / CLOCKS_PER_SEC - sta << "\n";
return 0;
}
P5072 [Ynoi2015] 盼君勿忘
给定序列 \(a_n\),\(m\) 次询问给一个区间 \([l_i, r_i]\),对于它的每个子序列 \(S\),将 \(S\) 去重后求和,答案对 \(p_i\) 取模。
\(1 \leq n, m, a_i \leq 10^5, 1 \leq p \leq 10^9\)。
莫队。
考虑每个元素对答案的贡献。设元素 \(x\) 的出现次数为 \(c\),\(len = r - l + 1\),那么贡献是 \(x(2^c-1)2^{len-c} = x2^{len} - x2^{len - c}\)。前者只需要维护不同数的和,后者考虑按照 \(c\) 根号分治。
\(c \geq \sqrt n\) 的数,不超过 \(\sqrt n\) 个,暴力计算。
\(c < \sqrt n\) 的,每个维护一下 \(\sum x\) 即可。
需要 \(O(1)\) 计算 \(p_i^k\),根号预处理 \(p^k, \big(p^{\sqrt n}\big)^k\) 即可。
// Author: HolyK
// Created: Wed Sep 8 16:32:52 2021
#include <bits/stdc++.h>
template <class T, class U>
inline bool smin(T &x, const U &y) {
return y < x ? x = y, 1 : 0;
}
template <class T, class U>
inline bool smax(T &x, const U &y) {
return x < y ? x = y, 1 : 0;
}
using LL = long long;
using PII = std::pair<int, int>;
constexpr int N(1e5 + 5), T(384);
int n, m, a[N];
struct Qry {
int l, r, p, id;
bool operator<(const Qry &rhs) const {
return l / T == rhs.l / T ? l / T & 1 ? r < rhs.r : r > rhs.r : l < rhs.l;
}
} q[N];
int cnt[N];
, sum[N];
LL svoid add(int x) {
if (!cnt[x]) s += x;
[cnt[x]] -= x;
sum[++cnt[x]] += x;
sum}
void del(int x) {
[cnt[x]] -= x;
sum[--cnt[x]] += x;
sumif (!cnt[x]) s -= x;
}
std::vector<int> big;
int ans[N], pw[T], ppw[T];
int main() {
// freopen("t.in", "r", stdin);
// freopen(".out", "w", stdout);
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n >> m;
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
[a[i]]++;
cnt}
for (int i = 1; i <= 1e5; i++) {
if (cnt[i] >= T) {
.push_back(i);
big}
}
for (int i = 1; i <= m; i++) {
std::cin >> q[i].l >> q[i].r >> q[i].p;
[i].id = i;
q}
std::sort(q + 1, q + 1 + m);
(cnt, 0, sizeof cnt);
memsetfor (int i = 1, x = 1, y = 0; i <= m; i++) {
while (x > q[i].l) add(a[--x]);
while (y < q[i].r) add(a[++y]);
while (x < q[i].l) del(a[x++]);
while (y > q[i].r) del(a[y--]);
int len = q[i].r - q[i].l + 1;
// std::cerr << "[Q] " << q[i].l << " " << q[i].r << ":\n";
[0] = 1;
pwconst int &p = q[i].p;
for (int i = 1; i < T; i++) pw[i] = 2LL * pw[i - 1] % p;
[0] = 1;
ppw[1] = pw[T - 1] * 2LL % p;
ppwfor (int i = 2; i < T; i++) ppw[i] = 1LL * ppw[i - 1] * ppw[1] % p;
auto power = [&](int k) {
return 1LL * ppw[k / T] * pw[k % T] % p;
};
auto &r = ans[q[i].id];
// std::cerr << s << "\n";
= s % p * power(len) % p;
r for (int x : big) {
if (cnt[x] >= T) {
= (r - 1LL * x * power(len - cnt[x])) % p;
r }
}
for (int i = std::min(T - 1, len), c = power(len - i); i; i--, c = 2LL * c % p) {
if (sum[i]) {
= (r - sum[i] % p * c) % p;
r }
}
if (r < 0) r += p;
}
for (int i = 1; i <= m; i++) {
std::cout << ans[i] << "\n";
}
return 0;
}
P5397 [Ynoi2018] 天降之物
给定序列 \(a_n\),\(m\) 次操作:将所有 \(x\) 改成 \(y\);找出 \((i, j)\) 满足 \(a_i = x, a_j = y\),求 \(\min |i - j|\)。
所有数在 \([1, 10^5]\) 范围内,强制在线。
按照出现次数根号分治,大于等于 \(\sqrt n\) 的称为大,否则称为小。
如果没有修改,可以预处理大对其他值的答案 \(ans\),询问是有大就查表,否则就归并。
考虑修改,首先用并查集维护每个值的位置集合,每次修改值的时候只要合并并查集就行了,这样方便后面快速得到每个位置的值。
小值维护位置集合,小改为小时,如果合起来还是小,直接归并;否则产生了一个大,\(O(n)\) 处理它对其他值的答案。
小改为大(大改为小可以交换两个值)时,直接归并复杂度是 \(O(n)\),所以要再摊一下。
对于每个大值维护一个附属位置集合作为缓冲,小值位置集合先向大值附属集合归并,如果归并后大小 \(\geq \sqrt n\),则直接 \(O(n)\) 重构大值。
此时 \(ans\) 只维护大值非附属集合对其他值的答案,附属集合由于大小不超过 \(\sqrt n\),它产生的贡献在查询时可以直接归并查询。
复杂度 \(O(n \sqrt n)\)。
// Author: HolyK
// Created: Thu Sep 16 08:09:38 2021
#include <bits/stdc++.h>
template <class T, class U>
inline bool smin(T &x, const U &y) {
return y < x ? x = y, 1 : 0;
}
template <class T, class U>
inline bool smax(T &x, const U &y) {
return x < y ? x = y, 1 : 0;
}
using LL = long long;
using PII = std::pair<int, int>;
constexpr int N(1e5 + 5), T(512), S(N / T + 1);
template <int S>
struct LazyArray {
int a[S], b[S], c;
void clear() { c++; }
int &operator[](const int &i) {
if (b[i] != c) b[i] = c, a[i] = 0;
return a[i];
}
};
int n, m, a[N], s[N];
std::vector<int> g[N], h[N];
int id[N], cnt, trash[N], *cur = trash, val[N], ans[S][N];
int newId() {
return trash == cur ? ++cnt : *cur--;
}
void del(int x) {
if (!id[x]) return;
*++cur = id[x];
(ans[id[x]], 0x3f, sizeof ans[id[x]]);
memset[id[x]] = 0;
val[x] = 0;
id}
int cal(const std::vector<int> &a, const std::vector<int> &b) {
int res = 1e9;
for (int i = 0, j = 0; i < a.size() || j < b.size(); ) {
if (j == b.size() || i < a.size() && a[i] < b[j]) {
if (j) smin(res, a[i] - b[j - 1]);
++;
i} else {
if (i) smin(res, b[j] - a[i - 1]);
++;
j}
}
return res;
}
int fa[N], root[N];
int find(int x) {
while (x != fa[x]) x = fa[x] = fa[fa[x]];
return x;
}
void update(int x) {
int y = id[x];
for (int i = 1, p = 0; i <= n; i++) {
[i] = a[find(i)];
aif (a[i] == x) {
= i;
p } else if (p) {
(ans[y][a[i]], i - p);
smin}
}
for (int i = n, p = 0; i; i--) {
if (a[i] == x) {
= i;
p } else if (p) {
(ans[y][a[i]], p - i);
smin}
}
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n >> m;
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
[a[i]]++;
s}
for (int i = 1; i <= 1e5; i++) {
if (s[i] >= T) {
[i] = newId();
id[id[i]] = i;
val}
}
for (int i = 1; i <= n; i++) {
if (s[a[i]] < T) g[a[i]].push_back(i);
[a[i]] = i;
root}
for (int i = 1; i <= n; i++) {
[i] = root[a[i]];
fa}
(ans, 0x3f, sizeof ans);
memsetfor (int i = 1; i <= cnt; i++) {
(val[i]);
update}
int res = 0;
while (m--) {
int opt, x, y;
std::cin >> opt >> x >> y;
^= res, y ^= res;
x static int tmp[N];
auto mergeTo = [&](std::vector<int> &a, std::vector<int> &b) {
std::merge(a.begin(), a.end(), b.begin(), b.end(), tmp);
.resize(a.size() + b.size());
b.clear();
astd::copy_n(tmp, b.size(), b.begin());
};
// std::cerr << x << " " << y << " " << s[x] << " " << s[y] << "\n";
if (opt == 1) {
if (x == y || !s[x]) continue;
if (!root[y]) {
[y] = root[x];
root[root[y]] = y;
a} else {
[root[x]] = root[y];
fa}
[x] = 0;
rootfor (int i = 1; i <= cnt; i++) {
(ans[i][y], ans[i][x]);
smin[i][x] = 0x3f3f3f3f;
ans}
if (s[x] < T && s[y] < T) {
[y] += s[x], s[x] = 0;
sif (s[y] >= T) {
[y] = newId();
id[id[y]] = y;
val[x].clear(), g[y].clear();
g
(y);
update
} else {
(g[x], g[y]);
mergeTo}
continue;
}
if (s[x] >= T && s[y] < T || s[x] < T && s[y] >= T) {
if (s[x] > s[y]) {
std::swap(s[x], s[y]);
std::swap(id[x], id[y]);
std::swap(g[x], g[y]);
std::swap(h[x], h[y]);
[id[y]] = y;
val}
if (h[y].size() + s[x] < T) {
[y] += s[x], s[x] = 0;
s(g[x], h[y]);
mergeTocontinue;
}
}
[x].clear(), g[y].clear();
g[x].clear(), h[y].clear();
h(x);
del[y] += s[x], s[x] = 0;
s(y);
update} else {
if (!s[x] || !s[y]) {
= 0;
res std::cout << "Ikaros\n";
continue;
}
if (x == y) {
= 0;
res std::cout << "0\n";
continue;
}
if (s[x] < T && s[y] < T) {
= cal(g[x], g[y]);
res } else if (s[x] >= T && s[y] >= T) {
= std::min(ans[id[x]][y], ans[id[y]][x]);
res (res, cal(h[x], h[y]));
smin} else {
if (s[x] < s[y]) std::swap(x, y);
= ans[id[x]][y];
res (res, cal(h[x], g[y]));
smin}
std::cout << res << "\n";
}
}
return 0;
}
P5398 [Ynoi2018] GOSICK
给定序列 \(a_n\),\(m\) 次询问,给定区间 \([l, r]\),查询二元组 \((i, j)\) 的个数,满足 \(i, j \in [l, r], a_i \mid a_j\)。
\(1 \leq n, m, a_i \leq 10^5\)。
莫队二次离线。
加入或删除一个数,就查询该数在当前区间内的倍数和约数个数,显然可以通过差分搞成两个前缀之差。
发现枚举倍数复杂度炸了,所以把小数的贡献先搞掉。
令 \(T = \sqrt n\),计算每个 \(x < T\) 对每个询问的贡献。对于每个 \(x\),可以通过前缀和 \(O(n + m)\) 完成。
然后大数的因数和倍数都是 \(\sqrt n\) 级别的了,直接 \(\sqrt n\) 修改 \(O(1)\) 查询即可。
// Author: HolyK
// Created: Sun May 30 14:48:18 2021
#include <bits/stdc++.h>
#define dbg(a...) fprintf(stderr, a)
template <class T, class U>
inline bool smin(T &x, const U &y) {
return y < x ? x = y, 1 : 0;
}
template <class T, class U>
inline bool smax(T &x, const U &y) {
return x < y ? x = y, 1 : 0;
}
using LL = long long;
using PII = std::pair<int, int>;
inline char gc() {
static constexpr int BufferSize = 1 << 22 | 5;
static char buf[BufferSize], *p, *q;
static std::streambuf *i = std::cin.rdbuf();
return p == q ? p = buf, q = p + i->sgetn(p, BufferSize), p == q ? EOF : *p++ : *p++;
}
inline void pc(char c) {
static std::streambuf *o = std::cout.rdbuf();
->sputc(c);
o}
struct Reader {
template <class T>
&operator>>(T &w) {
Reader char c, p = 0;
for (; !std::isdigit(c = gc());) if (c == '-') p = 1;
for (w = c & 15; std::isdigit(c = gc()); w = w * 10 + (c & 15)) ;
if (p) w = -w;
return *this;
}
} cin;
struct Putter {
template <class T>
&operator<<(T w) {
Putter if (w == 0) return pc('0'), *this;
if (w < 0) pc('-'), w = -w;
static char s[20], *t;
for (t = s; w; w /= 10) *++t = w % 10 + '0';
while (t != s) pc(*t--);
return *this;
}
&operator<<(const char *s) {
Putter while (*s) pc(*s++);
return *this;
}
} cout;
constexpr int T = 1024, N(5e5 + 5);
struct Qry {
int l, r, id;
bool operator<(const Qry &rhs) const {
return l / T == rhs.l / T ? l / T & 1 ? r < rhs.r : r > rhs.r : l < rhs.l;
}
} q[N];
int a[N], c[N], d[N], t1[N], t2[N];
[N], pre1[N], ans[N], out[N];
LL prestd::array<int, 4> g[N << 1];
std::vector<int> fac[N];
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m, max = 0;
>> n >> m;
cin for (int i = 0; i < n; i++) cin >> a[i], smax(max, a[i]);
for (int i = 0; i < m; i++) {
>> q[i].l >> q[i].r;
cin [i].l--, q[i].r--;
q[i].id = i;
q}
= 1e18, sum = 0;
LL min int t = 1;
for (int i = 0; i < n; i++) c[a[i]]++;
for (int i = max; i; i--) {
if (smin(min, sum + 1LL * i * n * 3)) t = i;
+= (LL)max / i * c[i];
sum }
for (int i = t + 1; i <= max; i++) {
for (int j = i; j <= max; j += i) {
[j].push_back(i);
fac}
}
for (int i = 1; i <= t; i++) {
(t1, 0, n << 2);
memset(t2, 0, n << 2);
memsetfor (int j = 0; j < n; j++) {
[j] = a[j] % i == 0;
t1[j] = a[j] == i;
t2}
for (int j = 1; j < n; j++) {
[j] += t1[j - 1];
t1[j] += t2[j - 1];
t2}
for (int j = 0; j < m; j++) {
auto [l, r, id] = q[j];
[id] += 1LL * (t1[r] - (l ? t1[l - 1] : 0) - 1) * (t2[r] - (l ? t2[l - 1] : 0));
out}
}
for (int i = 0; i < n; i++) if (a[i] <= t) a[i] = 0;
auto clr = [&]() {
(c, 0, max + 1 << 2);
memset};
auto add = [&](int x) {
if (!x) return;
for (auto i : fac[x]) c[i]++;
for (int i = x; i <= max; i += x) c[i]++;
};
auto ask = [&](int x) {
return c[x];
};
();
clrfor (int i = 0; i < n - 1; i++) {
(a[i]);
add[i + 1] = ask(a[i]) + pre[i];
pre[i + 1] = ask(a[i + 1]) + pre1[i];
pre1}
std::sort(q, q + m);
int x = 1, y = 0, cnt = 0;
for (int i = 0; i < m; i++) {
if (x < q[i].l) {
[cnt++] = {y, x, q[i].l - 1, -i - 1};
g}
if (x > q[i].l) {
[cnt++] = {y, q[i].l, x - 1, i};
g}
[i] += pre[q[i].l] - pre[x];
ans= q[i].l;
x if (x) {
if (y < q[i].r) {
[cnt++] = {x - 1, y + 1, q[i].r, -i - 1};
g}
if (y > q[i].r) {
[cnt++] = {x - 1, q[i].r + 1, y, i};
g}
}
[i] += pre1[q[i].r] - pre1[y];
ans= q[i].r;
y }
std::sort(g, g + cnt);
();
clrfor (int i = 0, p = 0; i < n && p < cnt; i++) {
(a[i]);
addfor (; p < cnt && g[p][0] == i; p++) {
auto [k, l, r, id] = g[p];
= 0;
LL t for (int j = l; j <= r; j++) t += ask(a[j]);
if (id < 0) {
[-id - 1] -= t;
ans} else {
[id] += t;
ans}
}
}
for (int i = 1; i < m; i++) ans[i] += ans[i - 1];
for (int i = 0; i < m; i++) out[q[i].id] += ans[i] + q[i].r - q[i].l + 1;
for (int i = 0; i < m; i++) std::cout << out[i] << "\n";
return 0;
}
P5399 [Ynoi2018] 駄作
给定 \(n\) 个节点的树,边权为 1。\(m\) 次询问,给定 \(p_0, d_0, p_1, d_1\),计算 \[ \sum_{d(p_0, a) \leq d_0}\sum_{d(p_1, b) \leq d_1} d(a, b) \]
\(1 \leq n, m \leq 10^5\)
Top cluster 树分块。
这个题前前后后写了几天,还是常数太大过不了。
每个邻域可以被分成每个块的邻域,只有 \(p_0, p_1\) 所在块的起点不是界点,其余都是。
不同块邻域之间的贡献,可以在收缩树上DP,这里需要预处理每个块以界点为起点的邻域的各种信息。
相同块邻域之间的贡献,如果起点不是界点,这样的情况只有 \(O(m)\) 次,在块内 DP,复杂度 \(O(m \sqrt n)\)。
如果起点是界点,将询问离线,对于每个块,处理以界点为起点的邻域之间的答案。可以转换为求 \(\sum d(LCA)\),然后搞 \(O(B)\) 修改,\(O(1)\) 查询。
我的实现用到了预处理块与块的距离,点到界点的距离,块内 \(O(1)\) rmq lca 的方式来搞,常数太大了。
怎么卡常:通过阅读其他人的代码,可以发现,对于每一块,按照bfs序重新标号,这样每次寻找邻域时用bfs就比较 cache 友好,然后对于收缩树,我的代码中是直接以界点构成的树来DP的,但是好像别人是用块构成的树来DP的,所以我还要处理 rake 块的小情况,十分麻烦。
// Author: HolyK
// Created: Mon Mar 28 17:33:17 2022
#include <bits/stdc++.h>
template <class T, class U>
inline bool smin(T &x, const U &y) {
return y < x ? x = y, 1 : 0;
}
template <class T, class U>
inline bool smax(T &x, const U &y) {
return x < y ? x = y, 1 : 0;
}
inline char gc() {
static constexpr int BufferSize = 1 << 22 | 5;
static char buf[BufferSize], *p, *q;
static std::streambuf *i = std::cin.rdbuf();
return p == q ? p = buf, q = p + i->sgetn(p, BufferSize), p == q ? EOF : *p++ : *p++;
}
struct Reader {
template <class T>
&operator>>(T &w) {
Reader char c, p = 0;
for (; !std::isdigit(c = gc());) if (c == '-') p = 1;
for (w = c & 15; std::isdigit(c = gc()); w = w * 10 + (c & 15)) ;
if (p) w = -w;
return *this;
}
} cin;
using LL = long long;
using PII = std::pair<int, int>;
std::mt19937_64 rng((std::random_device())());
(LL l, LL r) {
LL rndif (l > r) std::swap(l, r);
return std::uniform_int_distribution<LL>(l, r)(rng);
}
constexpr int N(1e5 + 5), T(666), K(N / T + 5), S(N / T * 6 + 5);
std::vector<int> g[N];
int n, m, root, fa[N], dep[N], siz[N], bot[N];
std::vector<int> g1[N], pt[N];
int key[N], keys[N], fa1[N];
int gl[N], gr[N], bel[N], tot, end[N][2];
int in[N], st[10][N], lg[N];
void dfs(int x) {
int cnt = 0;
static int s[N], t;
for (int y : g[x]) {
[y] = x;
fa[y].erase(std::find(g[y].begin(), g[y].end(), x));
g[t++] = y;
s[y] = dep[x] + 1;
dep(y);
dfs[x] += siz[y];
sizif (bot[y]) {
[x] = bot[y];
bot++;
cnt}
}
if (x == root || siz[x] + 1 >= T || cnt > 1) {
[x] = ++keys[0];
key[keys[0]] = x;
keys[x] = x;
bot[x] = 0;
siz
int r = g[x].size(), c = 0, now = 0;
for (int i = g[x].size() - 1; i >= 0; i--) {
int y = g[x][i];
if (c && bot[y] || now + siz[y] >= T) {
int tt = t;
do {
[s[--t]] = tot;
bel[s[t]] = in[0]--;
in} while (s[t] != g[x][i + 1]);
[tot].assign(s + t, s + tt);
pt[tot] = i + 1, gr[tot] = r;
gl= i + 1;
r [tot][0] = x;
end[tot][1] = c;
end++;
tot
= c = 0;
now }
+= siz[y];
now if (bot[y]) {
= y = bot[y];
c [x].push_back(y);
g1[y].push_back(x);
g1[y] = x;
fa1}
}
int tt = t;
do {
[s[--t]] = tot;
bel[s[t]] = in[0]--;
in
} while (s[t] != g[x][0]);
[tot].assign(s + t, s + tt);
pt[tot] = 0, gr[tot] = r;
gl[tot][0] = x;
end[tot][1] = c;
end++;
tot}
[x]++;
siz}
int get(int x, int y) {
return in[x] < in[y] ? x : y;
}
int lca(int x, int y) {
if (x == y) return x;
= in[x], y = in[y];
x if (x > y) std::swap(x, y);
int k = lg[y - x];
return get(st[k][x], st[k][y - (1 << k)]);
}
int dis(int x, int y) {
return dep[x] + dep[y] - dep[lca(x, y)] * 2;
}
void initLCA() {
for (int i = 1; i <= n; i++) {
[0][in[i] - 1] = fa[i];
st}
for (int i = 2; i <= n; i++) {
[i] = lg[i >> 1] + 1;
lg}
for (int i = 1; i < 10; i++) {
for (int j = 1; j + (1 << i) - 1 <= n; j++) {
[i][j] = get(st[i - 1][j], st[i - 1][j + (1 << i - 1)]);
st}
}
}
int vis[N], cnt[S][T][2], d2k[N][2], dk[K][K];
[S][T][2][2];
LL sum
std::vector<PII> pts[S][2];
void init() {
static PII q[N];
int l, r;
for (int i = 1; i <= n; i++) {
if (!key[i]) continue;
int o = key[i];
[l = r = 1] = {i, 0};
q[i] = ++vis[0];
viswhile (l <= r) {
auto [x, z] = q[l++];
[o][key[x]] = z;
dkfor (int y : g1[x]) {
if (vis[y] == vis[0]) continue;
[y] = vis[0];
vis[++r] = {y, z + std::abs(dep[x] - dep[y])};
q}
}
}
for (int i = 0; i < tot; i++) {
int p = end[i][0], p1 = end[i][1];
static int c[2][T];
(c, 0, sizeof c);
memsetfor (int x : pt[i]) {
int dx = d2k[x][0] = dep[x] - dep[p];
[0][d2k[x][0]]++;
c[i][dx][0]++;
cnt[i][dx][0][0] += dx;
sumif (p1) {
int dy = d2k[x][1] = dis(x, p1);
[1][d2k[x][1]]++;
c[i][dy][1]++;
cnt[i][dx][0][1] += dy;
sum[i][dy][1][0] += dx;
sum[i][dy][1][1] += dy;
sum}
}
[i][0].resize(pt[i].size());
ptsif (p1) pts[i][1].resize(pt[i].size());
for (int j = 1; j < T; j++) c[0][j] += c[0][j - 1], c[1][j] += c[1][j - 1];
for (int u = pt[i].size() - 1; u >= 0; u--){
int x = pt[i][u];
[i][0][--c[0][d2k[x][0]]] = {x, d2k[x][0]};
ptsif (p1) pts[i][1][--c[1][d2k[x][1]]] = {x, d2k[x][1]};
}
for (int j = 1; j < T; j++) {
for (int k : {0, 1}) {
[i][j][k] += cnt[i][j - 1][k];
cnt[i][j][k][0] += sum[i][j - 1][k][0];
sum[i][j][k][1] += sum[i][j - 1][k][1];
sum}
}
}
}
std::array<int, 4> qry[N];
struct Info {
int c;
[2];
LL svoid clear() {
= 0;
c [0] = s[1] = 0;
s}
} rake[N][2], comp[N][2];
[N];
LL ansvoid dp(int x, int id) {
[id] += rake[x][0].c * rake[x][1].s[0] + rake[x][1].c * rake[x][0].s[0];
ans
for (int y : g1[x]) {
if (y == fa1[x]) continue;
(y, id);
dp
// compress
for (int i = 0; i < 2; i++) {
[id] += rake[y][i].c * comp[y][!i].s[1] + rake[y][i].s[0] * comp[y][!i].c;
ans}
for (int i = 0; i < 2; i++) {
[y][i].c += rake[y][i].c;
comp[y][i].s[0] += 1LL * rake[y][i].c * (dep[y] - dep[x]) + rake[y][i].s[0];
comp}
// rake
for (int i = 0; i < 2; i++) {
[id] += rake[x][i].c * comp[y][!i].s[0] + rake[x][i].s[0] * comp[y][!i].c;
ans
}
for (int i = 0; i < 2; i++) {
[x][i].c += comp[y][i].c;
rake[x][i].s[0] += comp[y][i].s[0];
rake}
}
}
void split(int x, int d, int k) {
if (x == root) {
[root][k].c++;
rakefor (int i = 0; i < tot; i++) {
auto &f = end[i][1] ? comp[end[i][1]][k] : rake[end[i][0]][k];
int q = dep[end[i][0]];
if (q > d) continue;
= std::min(T - 1, d - q);
q .c += cnt[i][q][0];
f.s[0] += sum[i][q][0][0];
f.s[1] += sum[i][q][0][1];
f}
return;
}
int o = bel[x], ou = key[end[o][0]], od = key[end[o][1]];
if (dep[x] <= d) rake[root][k].c++;
for (int i = 0; i < tot; i++) {
auto &f = end[i][1] ? comp[end[i][1]][k] : rake[end[i][0]][k];
if (i == o) {
if (key[x] || d >= T - 1) {
int r = key[x] ? 1 : 0, q = std::min(d, T - 1);
.c += cnt[i][q][r];
f.s[0] += sum[i][q][r][0];
f.s[1] += sum[i][q][r][1];
f} else {
for (auto v : pt[i]) {
if (dis(x, v) > d) continue;
.c++;
f.s[0] += d2k[v][0];
f.s[1] += d2k[v][1];
f}
}
continue;
}
int iu = key[end[i][0]], id = key[end[i][1]];
int r = 0, q = dk[ou][iu] + d2k[x][0];
if (od) smin(q, dk[od][iu] + d2k[x][1]);
if (id) {
int qq = dk[ou][id] + d2k[x][0];
if (od) smin(qq, dk[od][id] + d2k[x][1]);
if (smin(q, qq)) r = 1;
}
if (q > d) continue;
= std::min(T - 1, d - q);
q
.c += cnt[i][q][r];
f.s[0] += sum[i][q][r][0];
f.s[1] += sum[i][q][r][1];
f}
}
[N], s[N], ss, cc;
LL val
void getS(int x) {
if (key[x]) return;
for (int y : g[x]) {
if (y == fa[x]) continue;
[y] = s[x] + val[y];
s(y);
getS}
}
void update(int x, int y) {
while (!key[fa[x]]) {
[x] += y;
val= fa[x];
x }
[x] = val[x] += y;
s(x);
getS// std::cerr << "update " << x << "\n";
}
struct Node {
int d, id;
;
PII a2};
void cal(int o) {
int p = end[o][0], p1 = end[o][1], ou = key[p], od = key[p1];
int coef = p1 ? 1 : 0;
std::vector<Node> b[2];
for (int i = 1; i <= m; i++) {
[2];
PII afor (int k : {0, 1}) {
int x = qry[i][k * 2], d = qry[i][k * 2 + 1];
if (x == root) {
if (d <= dep[p]) goto ed;
[k] = {0, d - dep[p]};
a} else if (bel[x] == o) {
if (d >= T - 1 || key[x]) {
[k] = {key[x] ? 1 : 0, d};
a} else {
[k] = {-x, d};
a}
} else {
int r = 0, i = bel[x], iu = key[end[i][0]], id = key[end[i][1]], q = dk[ou][iu] + d2k[x][0];
if (id) smin(q, dk[ou][id] + d2k[x][1]);
if (od) {
int qq = dk[od][iu] + d2k[x][0];
if (id) smin(qq, dk[od][id] + d2k[x][1]);
if (smin(q, qq)) r = 1;
}
if (q > d || q == d && !r) goto ed;
[k] = {r, d - q};
a}
if (a[k].first >= 0) {
(a[k].second, pts[o][a[k].first].back().second);
smin}
}
if (a[0].first < 0) {
std::swap(a[0], a[1]);
}
if (a[0].first < 0) {
// std::cerr << "AA\n";
int x1 = -a[0].first, d1 = a[0].second, x2 = -a[1].first, d2 = a[1].second;
static int c[N][2];
[p][0] = c[p][1] = 0;
cfor (int x : pt[o]) {
[x][0] = dis(x, x1) <= d1;
c[x][1] = dis(x, x2) <= d2;
c}
if (p1) {
static LL s[N][2];
[p][0] = s[p][1] = 0;
sfor (int x : pt[o]) s[x][0] = s[x][1] = 0;
for (int j = pts[o][0].size() - 1; j >= 0; j--) {
int x = pts[o][0][j].first, y = fa[x];
[x][0] += c[x][0], s[x][1] += c[x][1];
s[i] += c[x][0] * s[y][1] + s[x][0] * c[y][1] +
ans[x][1] * s[y][0] + s[x][1] * c[y][0];
c
[y][0] += c[x][0], c[y][1] += c[x][1];
c[y][0] += s[x][0], s[y][1] += s[x][1];
s}
} else {
for (int j = pts[o][0].size() - 1; j >= 0; j--) {
int x = pts[o][0][j].first, y = fa[x];
[i] -= 2LL * c[x][0] * c[x][1];
ans[y][0] += c[x][0], c[y][1] += c[x][1];
c}
}
} else {
[a[0].first].push_back({a[0].second, i, a[1]});
b}
:;
ed}
for (int z : {0, 1}) {
if (b[z].empty()) continue;
int m = 0;
for (auto &v : b[z]) smax(m, v.d);
std::vector<int> c(m + 1);
for (auto &v : b[z]) c[v.d]++;
for (int i = 1; i <= m; i++) c[i] += c[i - 1];
std::vector<Node> bb(b[z].size());
for (auto &v : b[z]) bb[--c[v.d]] = v;
for (int i = 0, j, k = 0; i < bb.size(); i = j) {
int d = bb[i].d, m[2] = {};
static LL sum[2][T];
for (j = i; j < bb.size() && d == bb[j].d; j++) {
if (bb[j].a2.first >= 0) smax(m[bb[j].a2.first], bb[j].a2.second);
}
while (k < pts[o][z].size() && pts[o][z][k].second <= d) {
int x = pts[o][z][k++].first;
(x, 1);
update+= d2k[x][0];
ss += 1;
cc }
for (int i = 0; i <= m[0]; i++) sum[0][i] = 0;
for (int i = 0; i <= m[1]; i++) sum[1][i] = 0;
for (auto [x, d] : pts[o][0]) {
if (d > m[0]) break;
[0][d] += coef * (ss + 1LL * cc * d) - s[x] * 2;
sum}
for (int i = 1; i <= m[0]; i++) sum[0][i] += sum[0][i - 1];
for (auto [x, d] : pts[o][1]) {
if (d > m[1]) break;
[1][d] += coef * (ss + 1LL * cc * d2k[x][0]) - s[x] * 2;
sum}
for (int i = 1; i <= m[1]; i++) sum[1][i] += sum[1][i - 1];
for (int k = i; k < j; k++) {
auto [x, d] = bb[k].a2;
if (x >= 0) {
// assert(d <= m[x]);
[bb[k].id] += sum[x][d];
ans} else {
for (int v : pt[o]) {
if (dis(v, -x) <= d) {
[bb[k].id] += coef * (ss + 1LL * cc * d2k[v][0]) - s[v] * 2;
ans}
}
}
}
}
for (int x : pt[o]) s[x] = val[x] = 0, ss = cc = 0;
}
}
void solve() {
>> n;
cin for (int i = 2, x; i <= n; i++) {
>> x;
cin [x].push_back(i);
g[i].push_back(x);
g}
= rnd(1, n);
root [root] = -1;
bel[0] = n, in[root] = 1;
in(root);
dfs
// for (int i = 1; i <= n; i++) {
// std::cerr << i << " " << bel[i] << "\n";
// }
();
initLCA
// std::cerr << "??\n";
for (int i = 1; i <= n; i++) {
if (i != root)
[i].push_back(fa[i]);
g}
();
init
std::cerr << (double)clock() / CLOCKS_PER_SEC << "\n";
>> m;
cin for (int i = 1; i <= m; i++) {
int x1, d1, x2, d2;
>> x1 >> d1 >> x2 >> d2;
cin [i] = {x1, d1, x2, d2};
qryfor (int j = 1; j <= keys[0]; j++) {
int x = keys[j];
[x][0].clear();
rake[x][1].clear();
rake[x][0].clear();
comp[x][1].clear();
comp}
(x1, d1, 0), split(x2, d2, 1);
split(root, i);
dp}
std::cerr << (double)clock() / CLOCKS_PER_SEC << "\n";
for (int i = 0; i < tot; i++) {
(i);
cal}
for (int i = 1; i <= m; i++) {
std::cout << ans[i] << "\n";
}
std::cerr << (double)clock() / CLOCKS_PER_SEC << "\n";
}
int main() {
// freopen("t.in", "r", stdin);
// freopen("t1.out", "w", stdout);
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t = 1;
// std::cin >> t;
while (t--) {
();
solve}
return 0;
}
P4119 [Ynoi2018] 未来日记
给定序列 \(a_n\),\(m\) 次操作,区间 \([l, r]\) 所有 \(x\) 改成 \(y\);查区间 \([l, r]\) 的第 \(k\) 小值。
\(1 \leq n, m, a_i \leq 10^5\)
序列分块+值域分块。
每个值、每个值域块维护序列块中出现次数的前缀和。
用并查集维护每个序列快中每个值的位置集合。
修改时重新计算对应的 \(O(1)\) 个值和值域块的前缀和。
查询时两边散块重新统计值和值域块的出现次数,中间整块直接查表。
然后先确定答案所在的值域块,再确定具体的值。
复杂度 \(O(n \sqrt n)\)。
// Author: HolyK
// Created: Fri Sep 17 08:24:37 2021
#include <bits/stdc++.h>
template <class T, class U>
inline bool smin(T &x, const U &y) {
return y < x ? x = y, 1 : 0;
}
template <class T, class U>
inline bool smax(T &x, const U &y) {
return x < y ? x = y, 1 : 0;
}
using LL = long long;
using PII = std::pair<int, int>;
constexpr int N(1e5 + 5), T(512), S(N / T + 1);
int n, m, tot, a[N], fa[N], rt[S][N];
int b[N][S], c[S][S]; // Inter-block(single, block) prefix
int find(int x) {
while (x != fa[x]) x = fa[x] = fa[fa[x]];
return x;
}
int main() {
// freopen("t.in", "r", stdin);
// freopen("t.out", "w", stdout);
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n >> m;
= (n - 1 + T) / T;
tot
(rt, -1, sizeof rt);
memsetint max = 0;
for (int i = 0; i < n; i++) {
std::cin >> a[i];
[i / T][a[i]] = i;
rt[a[i]][i / T]++;
b[a[i] / T][i / T]++;
c(max, a[i]);
smax}
for (int i = 1; i <= max; i++) {
for (int j = 1; j < tot; j++) {
[i][j] += b[i][j - 1];
b}
}
for (int i = 0; i <= max / T; i++) {
for (int j = 1; j < tot; j++) {
[i][j] += c[i][j - 1];
c}
}
for (int i = 0; i < n; i++) {
[i] = rt[i / T][a[i]];
fa}
while (m--) {
int opt, l, r, x, y;
std::cin >> opt >> l >> r >> x;
--;
l
if (opt == 1) {
std::cin >> y;
if (x == y) continue;
static int tmp[S];
for (int o = l / T, i = o * T, j; i < r; o++, i += T) {
= std::min(n, T + i);
j int &u = rt[o][x], &v = rt[o][y];
if (u == -1) continue;
if (i < l || r < j) {
for (int k = i; k < j; k++) {
[k] = a[find(k)];
a}
= v = -1;
u
for (int k = std::max(l, i); k < j && k < r; k++) {
if (a[k] == x) a[k] = y, tmp[o]++;
}
for (int k = i; k < j; k++) {
if (a[k] == x) {
= k;
u } else if (a[k] == y) {
= k;
v }
}
for (int k = i; k < j; k++) {
[k] = rt[o][a[k]];
fa}
} else {
[o] = b[x][o] - (o ? b[x][o - 1] : 0);
tmpif (~v) {
[u] = v;
fa} else {
[v = u] = y;
a}
= -1;
u }
}
for (int i = l / T; i < tot; i++) {
if (i) tmp[i] += tmp[i - 1];
[x][i] -= tmp[i];
b[y][i] += tmp[i];
b[x / T][i] -= tmp[i];
c[y / T][i] += tmp[i];
c}
for (int i = l / T; i < tot; i++) {
[i] = 0;
tmp}
} else {
static int tmp[N], blo[S];
if (r - l <= T * 2) {
for (int i = l; i < r; i++) tmp[i] = a[find(i)];
// for (int i = l; i < r; i++) std::cerr << tmp[i] << " \n"[i + 1 == r];
std::nth_element(tmp + l, tmp + l + x - 1, tmp + r);
std::cout << tmp[l + x - 1] << "\n";
for (int i = l; i < r; i++) tmp[i] = 0;
continue;
}
--;
rint bl = l / T, br = r / T;
assert(bl + 1 < br);
for (int i = l; i < bl * T + T; i++) {
[i] = a[find(i)];
a[a[i]]++;
tmp[a[i] / T]++;
blo}
for (int i = br * T; i <= r; i++) {
[i] = a[find(i)];
a[a[i]]++;
tmp[a[i] / T]++;
blo}
int d = 0;
auto calSig = [&](int i) {
return tmp[i] + b[i][br - 1] - b[i][bl];
};
auto calBlo = [&](int i) {
return blo[i] + c[i][br - 1] - c[i][bl];
};
int z;
while (x > (z = calBlo(d))) {
-= z, d++;
x }
*= T;
d while (x > (z = calSig(d))) {
-= z, d++;
x }
std::cout << d << "\n";
for (int i = l; i < bl * T + T; i++) {
[a[i]] = 0;
tmp[a[i] / T] = 0;
blo}
for (int i = br * T; i <= r; i++) {
[a[i]] = 0;
tmp[a[i] / T] = 0;
blo}
}
}
return 0;
}
P4117 [Ynoi2018] 五彩斑斓的世界
给定序列 \(a_n\),\(m\) 次操作,区间大于 \(x\) 的减 \(x\);区间查询 \(x\) 的出现次数。
\(1 \leq n \leq 10^6, 1 \leq m \leq 5 \times 10^5, 0 \leq a, x \leq 10^5 + 1\)。
空间限制 64MB。
序列分块,每个块对答案的贡献是独立的,所以可以考虑将询问离线,每个块单独处理贡献,这样空间就比较小。
用并查集维护每个值出现的位置集合,然后考虑当前块的最大值 \(max\),
如果 \(x > max / 2\),那么操作之后最大值会变成 \(< x\),如果 \(x \leq max / 2\),那么操作之后最大值会减少 \(x\)。
即操作一次,最大值至少会减少 \(\min\{max - x, x\}\)。
- 如果 \(\min\{max-x, x\} \leq \sqrt
m\),我们用 \(O(\min\{max-x,
x\})\) 的时间来搞这个操作就行。
- 如果 \(max - x < x\),那么直接枚举 \(x + 1\cdots max\) 并查集合并就行。
- 如果 \(max - x > x\),可以先把 \(1 \cdots x\) 这些数加 \(x\),然后再整体打标记减 \(x\)。
- 如果 \(\min\{max-x, x\} > \sqrt m\),那么最多 \(\sqrt m\) 次就会把最大值减到 1,所以每次暴力减就行,复杂度 \(O(n\sqrt m)\)。
总复杂度 \(O(n\sqrt m + m\sqrt n)\)。
// Author: HolyK
// Created: Fri Sep 10 15:54:37 2021
#include <bits/stdc++.h>
template <class T, class U>
inline bool smin(T &x, const U &y) {
return y < x ? x = y, 1 : 0;
}
template <class T, class U>
inline bool smax(T &x, const U &y) {
return x < y ? x = y, 1 : 0;
}
using LL = long long;
using PII = std::pair<int, int>;
inline char gc() {
static constexpr int BufferSize = 1 << 22 | 5;
static char buf[BufferSize], *p, *q;
static std::streambuf *i = std::cin.rdbuf();
return p == q ? p = buf, q = p + i->sgetn(p, BufferSize), p == q ? EOF : *p++ : *p++;
}
struct Reader {
template <class T>
&operator>>(T &w) {
Reader char c, p = 0;
for (; !std::isdigit(c = gc());) if (c == '-') p = 1;
for (w = c & 15; std::isdigit(c = gc()); w = w * 10 + (c & 15)) ;
if (p) w = -w;
return *this;
}
} cin;
constexpr int N(1e6 + 5), T(888), M(2e5 + 5);
struct Option {
int opt, l, r, x;
} q[N];
int n, m, a[N], ans[N];
int pos[M];
int fa[T], siz[T];
int find(int x) {
while (x != fa[x]) x = fa[x] = fa[fa[x]];
return x;
}
int main() {
// freopen("t.in", "r", stdin);
// freopen("t.out", "w", stdout);
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
>> n >> m;
cin for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 1; i <= m; i++) {
>> q[i].opt >> q[i].l >> q[i].r >> q[i].x;
cin [i].l--;
q}
(pos, -1, sizeof pos);
memsetfor (int x = 0; x < n; x += T) {
int y = std::min(n, x + T), max, tag;
auto build = [&] {
= tag = 0;
max (siz, 0, sizeof siz);
memsetfor (int i = x; i < y; i++) {
(max, a[i]);
smax[a[i]] = i - x;
pos}
for (int i = x; i < y; i++) {
[i - x] = pos[a[i]];
fa[fa[i - x]]++;
siz}
};
();
buildfor (int i = 1; i <= m; i++) {
if (q[i].l >= y || q[i].r <= x) continue;
if (q[i].l <= x && y <= q[i].r) {
if (q[i].opt == 2) {
if (~pos[q[i].x + tag]) {
[i] += siz[pos[q[i].x + tag]];
ans}
continue;
}
if (max <= q[i].x || !q[i].x) continue;
if (std::min(max - q[i].x, q[i].x) <= T) {
if (max <= q[i].x * 2) {
for (int j = q[i].x + 1 + tag; j <= max + tag; j++) {
if (pos[j] == -1) continue;
int &u = pos[j], &v = pos[j - q[i].x];
if (v == -1) {
= u, a[x + u] -= q[i].x;
v } else {
[u] = v;
fa[v] += siz[u];
siz[u] = 0;
siz}
= -1;
u }
= q[i].x;
max } else {
for (int j = q[i].x + tag; j >= tag; j--) {
if (pos[j] == -1) continue;
int &u = pos[j], &v = pos[j + q[i].x];
if (v == -1) {
= u, a[x + u] += q[i].x;
v } else {
[u] = v;
fa[v] += siz[u];
siz[u] = 0;
siz}
= -1;
u }
+= q[i].x;
tag -= q[i].x;
max }
continue;
}
}
if (q[i].opt == 1 && max <= q[i].x) continue;
if (q[i].opt == 2 && pos[q[i].x + tag] == -1) continue;
for (int j = x; j < y; j++) {
[j] = a[x + find(j - x)];
a}
for (int j = x; j < y; j++) {
[a[j]] = -1;
pos[j] -= tag;
a}
int l = std::max(q[i].l, x), r = std::min(q[i].r, y);
if (q[i].opt == 1) {
for (int j = l; j < r; j++) {
if (a[j] > q[i].x) a[j] -= q[i].x;
}
} else {
for (int j = l; j < r; j++) {
if (a[j] == q[i].x) ans[i]++;
}
}
();
build}
for (int i = x; i < y; i++) {
if (fa[i - x] == i - x) pos[a[i]] = -1;
}
}
for (int i = 1; i <= m; i++) {
if (q[i].opt == 2) {
std::cout << ans[i] << "\n";
}
}
std::cerr << (double)clock() / CLOCKS_PER_SEC << "\n";
return 0;
}
P8205 [Ynoi2005] vti
\(n\) 个点的有根树,有边权,\(m\) 次询问,每次给出 \(t_i\) 个点,求它们的虚树包含的边的祖孙顺序对。
\(1 \leq n \leq 10^5, 1 \leq m \leq 10^6, \sum t_i \leq 10^6\).
可以通过一些简单计算,将问题转换为 \(O(\sum t_i)\) 次祖孙链查询。
树分块之后,因为都是祖孙链查询,考虑二次离线莫队。
左端点在同一块的,按照右端点dfs序排序,然后和普通莫队差不多。
需要讨论左右端点是否在同一块里面,两个需要分开计算。
二次离线莫队最后要根据前缀和计算答案,因为最后忘记区分这俩,调了一会。
需要求树上前缀的答案,这个用树状数组就行。
还要 \(O(1)\) 查询某个前缀比某个点小的数的个数,经典问题,搞一个 \(O(\sqrt n)\) 修改,\(O(1)\) 查询的数据结构。
// Author: HolyK
// Created: Sat Apr 2 20:50:00 2022
#include <bits/stdc++.h>
template <class T, class U>
inline bool smin(T &x, const U &y) {
return y < x ? x = y, 1 : 0;
}
template <class T, class U>
inline bool smax(T &x, const U &y) {
return x < y ? x = y, 1 : 0;
}
using LL = long long;
using PII = std::pair<int, int>;
constexpr int N(1e5 + 5), M(1e6 + 5);
int n, m;
std::vector<int> g[N];
int a[N], fa[N], in[N], out[N], dep[N], dfn;
namespace RMQLCA {
int st[17][N], lg[N];
int get(int x, int y) {
return in[x] < in[y] ? x : y;
}
void init() {
for (int i = 1; i <= n; i++) st[0][in[i] - 1] = fa[i];
for (int i = 2; i <= n; i++) lg[i] = lg[i >> 1] + 1;
for (int i = 1; i < 17; i++) {
for (int j = 1; j + (1 << i) - 1 <= n; j++) {
[i][j] = get(st[i - 1][j], st[i - 1][j + (1 << i - 1)]);
st}
}
}
int lca(int x, int y) {
if (x == y) return x;
= in[x], y = in[y];
x if (x > y) std::swap(x, y);
int k = lg[y - x];
return get(st[k][x], st[k][y - (1 << k)]);
}
}
using RMQLCA::lca;
[N], pre2[N];
LL pre1int c[N];
void dfs(int x) {
[x] = ++dfn;
infor (int p = a[x]; p <= n; p += p & -p) c[p]++;
for (int p = a[x] - 1; p; p -= p & -p) pre1[x] += c[p];
[x] += dep[x];
pre2for (int p = a[x]; p; p -= p & -p) pre2[x] -= c[p];
for (int y : g[x]) {
[y] = pre1[x];
pre1[y] = pre2[x];
pre2[y] = dep[x] + 1;
dep(y);
dfs}
[x] = dfn;
outfor (int p = a[x]; p <= n; p += p & -p) c[p]--;
}
int siz[N], bot[N], bel[N], tot;
bool key[N];
struct Qry {
int l, r, k, id;
bool operator<(const Qry &rhs) const {
return bel[l] == bel[rhs.l] ? in[r] < in[rhs.r] : bel[l] < bel[rhs.l];
}
} q[M * 2];
int qcnt, B;
[M * 2], oans[M];
LL ans
struct Block {
int top, bot;
} b[N];
void build(int x) {
static int q[N], r;
auto add = [&](int d) {
int o = tot++;
[o] = {x, d};
bfor (int i = 1; i <= r; i++) {
int u = q[i];
[u] = o;
belif (key[u]) continue;
for (int v : g[u]) q[++r] = v;
}
};
int s = 0, c = 0;
for (int y : g[x]) {
(y);
build+= siz[y];
s if (bot[y]) {
++;
c[x] = bot[y];
bot}
}
if (1 + s >= B || c > 1 || !x) {
std::sort(g[x].begin(), g[x].end(), [](int i, int j) {
return siz[i] < siz[j];
});
[x] = 1;
siz[x] = x;
bot[x] = true;
key= c = r = 0;
s
for (int y : g[x]) {
if (c && bot[y] || siz[y] + s >= B) {
(c);
add= c = r = 0;
s }
|= bot[y];
c += siz[y];
s [++r] = y;
q}
(c);
add} else {
[x] = 1 + s;
siz}
}
std::vector<Qry> q0[N], q1[N], q2[N];
constexpr int T(256);
struct SumDS {
int tot, s0[N], s1[N / T + 1];
void add(int p, int x) {
int k = p / T + 1, r = std::min(k * T, n + 1);
while (p < r) s0[p++] += x;
while (k < tot) s1[k++] += x;
}
int ask(int p) {
return s0[p] + s1[p / T];
}
} sumDS;
void dfsq(int x) {
.add(a[x], 1);
sumDSfor (auto &[l, r, k, id] : q0[x]) {
= 0;
LL s while (r != l) s += sumDS.ask(a[r] - 1), r = fa[r];
[id] += s * k;
oans}
for (auto &[l, r, k, id] : q1[x]) {
= 0;
LL s while (r != l) s += sumDS.ask(a[r] - 1), r = fa[r];
[id] += s * k;
ans}
for (auto &[l, r, k, id] : q2[x]) {
= 0;
LL s while (r != l) s += dep[x] - sumDS.ask(a[r]), r = fa[r];
[id] += s * k;
ans}
for (int y : g[x]) {
(y);
dfsq}
.add(a[x], -1);
sumDS}
void solve() {
std::cin >> n;
[1] = n;
afor (int i = 2; i <= n; i++) {
std::cin >> fa[i] >> a[i];
[fa[i]].push_back(i);
g}
[1] = 1;
dep(1);
dfs
::init();
RMQLCA
std::cin >> m;
for (int i = 1; i <= m; i++) {
int t;
std::cin >> t;
std::vector<int> p(t);
for (int j = 0; j < t; j++) std::cin >> p[j];
std::sort(p.begin(), p.end(), [](int i, int j) {
return in[i] < in[j];
});
for (int j = 0; j + 1 < t; j++) {
.push_back(lca(p[j], p[j + 1]));
p}
std::sort(p.begin(), p.end(), [](int i, int j) {
return in[i] < in[j];
});
.erase(std::unique(p.begin(), p.end()), p.end());
p
= p.size();
t std::vector<int> s = {0}, c(t);
for (int j = 1, x, u; j < t; j++) {
= p[j];
x for (;;) {
= p[s.back()];
u if (in[u] <= in[x] && in[x] <= out[u]) break;
.pop_back();
s}
[s.back()]++;
c.push_back(j);
s}
for (int j = 1; j < t; j++) {
[++qcnt] = {p[0], p[j], 1 - c[j], i};
q}
}
= n / sqrt(qcnt) * 1.5;
B
[0] = {1};
g(0);
build
std::sort(q + 1, q + 1 + qcnt);
for (int i = 1, j; i <= qcnt; i = j) {
int o = bel[q[i].l];
for (j = i + 1; j <= qcnt && o == bel[q[j].l]; j++) ;
for (int k = i, x = b[o].bot, y = b[o].bot; k < j; k++) {
if (bel[q[k].r] == o) {
[q[k].l].push_back({q[k].l, q[k].r, -q[k].k, q[k].id});
q0[q[k].id] += q[k].k * (pre1[q[k].r] - pre1[q[k].l]);
oans} else {
int l = q[k].l, r = q[k].r, t = lca(y, r);
if (t != y) {
[k] -= pre1[y] - pre1[t];
ans[x].push_back({t, y, 1, k});
q1= t;
y }
if (y != r) {
[k] += pre1[r] - pre1[y];
ans[x].push_back({y, r, -1, k});
q1= r;
y }
if (x != l) {
if (in[x] < in[l]) {
[k] += pre2[l] - pre2[x];
ans[y].push_back({x, l, -1, k});
q2} else {
[k] -= pre2[x] - pre2[l];
ans[y].push_back({l, x, 1, k});
q2}
= l;
x }
}
}
}
.tot = n / T + 1;
sumDS(1);
dfsq
for (int i = 1; i <= qcnt; i++) {
if (i > 1 && bel[q[i].l] == bel[q[i - 1].l]) {
[i] += ans[i - 1];
ans}
if (bel[q[i].l] != bel[q[i].r]) oans[q[i].id] += ans[i] * q[i].k;
}
for (int i = 1; i <= m; i++) {
std::cout << oans[i] << "\n";
}
}
int main() {
// freopen("t.in", "r", stdin);
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t = 1;
// std::cin >> t;
while (t--) {
();
solve}
return 0;
}
P6778 [Ynoi2009] rpdq
给定 \(n\) 个点的无根有边权的树,\(m\) 次询问,求 \[ \sum_{l \leq i < j \leq r} \operatorname{dis}(i, j) \]
\(1 \leq n, m, d \leq 2\times 10^5\)。
答案对 \(2^{32}\) 取模。
二次离线莫队之后转换成求一个点到前缀 \(1\dots k\) 的距离之和。转换成求它们的 lca 的深度之和。变成经典的链加链和,可以用树分块做到 \(O(\sqrt n)\) 修改,\(O(1)\) 查询。
这里树分块比较简单,只需要求出关键点,使得每个点向上最多跳 \(\sqrt n\) 步就能到达某个关键点。
之前卡常卡魔怔了,这个代码貌似用bfs序重新标了一下号,感觉很毒瘤。
// Author: HolyK
// Created: Mon Apr 11 10:09:33 2022
#include <bits/stdc++.h>
template <class T, class U>
inline bool smin(T &x, const U &y) {
return y < x ? x = y, 1 : 0;
}
template <class T, class U>
inline bool smax(T &x, const U &y) {
return x < y ? x = y, 1 : 0;
}
using LL = long long;
using PII = std::pair<int, int>;
inline char gc() {
static constexpr int BufferSize = 1 << 22 | 5;
static char buf[BufferSize], *p, *q;
static std::streambuf *i = std::cin.rdbuf();
return p == q ? p = buf, q = p + i->sgetn(p, BufferSize), p == q ? EOF : *p++ : *p++;
}
struct Reader {
template <class T>
&operator>>(T &w) {
Reader char c, p = 0;
for (; !std::isdigit(c = gc());) if (c == '-') p = 1;
for (w = c & 15; std::isdigit(c = gc()); w = w * 10 + (c & 15)) ;
if (p) w = -w;
return *this;
}
} cin;
constexpr int N(2e5 + 5), T(512), S(T * 4 + 5);
int n, m, B, siz[N], bot[N], fa[N], val[N], bup[N], bdn[N], kp[S];
int que[N], out[N], id[N], key[N], kid[S], kcnt;
unsigned dis[N], dep[N];
std::vector<PII> g[N];
void build(int x) {
static int l = 1, r = 0;
int c = 0;
for (auto [y, z] : g[x]) {
if (x) g[y].erase(std::find(g[y].begin(), g[y].end(), PII{x, z}));
[y] = x;
fa[y] = dep[x] + z;
dep(y);
build[x] += siz[y];
sizif (bot[y]) {
++;
c[x] = bot[y];
bot}
}
if (!x || siz[x] + 1 >= B || c > 1) {
[x] = ++kcnt;
key[x] = kcnt;
bot[x] = 0;
sizfor (auto [y, z] : g[x]) {
[++r] = y;
que[r] = z;
valif (bot[y]) {
[r] = z;
dis[bot[y]] = kcnt;
kp}
int u = r, bu = kcnt, bd = bot[y];
for (; l <= r; l++) {
int x = que[l];
[l] = bu;
bup[l] = bd;
bdnif (key[x]) continue;
for (auto [y, z] : g[x]) {
[++r] = y;
que[r] = z;
val[r] = dis[l];
disif (bot[y]) dis[r] += z;
}
}
[u] = r;
out}
}
[x]++;
siz}
void relabel() {
static int tmp[N];
#define R(a) for (int i = 0; i <= n; i++) tmp[i] = a[que[i]]; memcpy(a, tmp, sizeof tmp)
(bot);
R(fa);
R(key);
Rfor (int i = 1; i <= n; i++) id[que[i]] = i;
for (int i = 1; i <= n; i++) {
[i] = id[fa[i]];
faif (key[i]) {
[key[i]] = i;
kid}
}
}
unsigned v[N], s[N], ks[S], tag[S];
void add(int x) {
while (!key[fa[x]]) {
[x] += val[x];
v= fa[x];
x }
assert(out[x]);
[x] = v[x] += val[x];
sfor (int i = x + 1; i <= out[x]; i++) {
[i] = v[i] + s[fa[i]];
s}
= bup[x];
x while (x < kcnt) {
[x]++;
tag= kp[x];
x }
for (int i = kcnt - 1; i; i--) {
[i] = ks[kp[i]] + dis[kid[i]] * tag[i] + s[kid[i]];
ks}
}
unsigned ask(int x) {
return key[x] ? ks[key[x]] : dis[x] * tag[bdn[x]] + s[x] + ks[bup[x]];
}
unsigned pre[N], ans[N], oans[N];
struct Qry {
int l, r, id;
bool operator<(const Qry &rhs) const {
return l / T == rhs.l / T ? l / T & 1 ? r < rhs.r : r > rhs.r : l < rhs.l;
}
} q[N];
struct Q {
int l, r, k, id;
};
std::vector<Q> qq[N];
void solve() {
>> n >> m;
cin = 1.5 * sqrt(n);
B for (int i = 1, x, y, z; i < n; i++) {
>> x >> y >> z;
cin [x].push_back({y, z});
g[y].push_back({x, z});
g}
[0].push_back({n + 1 >> 1, 0});
g(0);
build();
relabelunsigned sum = 0;
for (int i = 1; i <= n; i++) {
(id[i]);
add+= dep[i];
sum [i] = pre[i - 1] + sum + i * dep[i] - 2 * ask(id[i]);
pre}
for (int i = 1; i <= m; i++) {
int l, r;
>> l >> r;
cin [i] = {l, r, i};
q}
std::sort(q + 1, q + 1 + m);
for (int i = 1, x = 1, y = 0; i <= m; i++) {
if (y != q[i].r) {
[i] += pre[q[i].r] - pre[y];
ansif (y < q[i].r) {
[x - 1].push_back({y + 1, q[i].r, -1, i});
qq} else {
[x - 1].push_back({q[i].r + 1, y, 1, i});
qq}
= q[i].r;
y }
if (x != q[i].l) {
[i] += pre[q[i].l - 1] - pre[x - 1];
ansif (x < q[i].l) {
[y].push_back({x, q[i].l - 1, -1, i});
qq} else {
[y].push_back({q[i].l, x - 1, 1, i});
qq}
= q[i].l;
x }
}
(v, 0, sizeof v);
memset(s, 0, sizeof s);
memset(ks, 0, sizeof ks);
memset(tag, 0, sizeof tag);
memset
= 0;
sum for (int i = 1; i <= n; i++) {
(id[i]);
add+= dep[i];
sum for (auto &[l, r, k, t] : qq[i]) {
= 0;
LL s for (int j = l; j <= r; j++) {
+= sum + i * dep[j] - 2 * ask(id[j]);
s }
[t] += k * s;
ans}
}
for (int i = 1; i <= m; i++) {
[i] += ans[i - 1];
ans[q[i].id] = ans[i];
oans}
for (int i = 1; i <= m; i++) {
std::cout << oans[i] << "\n";
}
}
int main() {
// freopen("t.in", "r", stdin);
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t = 1;
// std::cin >> t;
while (t--) {
();
solve}
return 0;
}
P5064 [Ynoi2014] 等这场战争结束之后
给你一个图,每个点有点权,最开始没有边。
有一些操作:
- 添加一条 \(x\) 与 \(y\) 之间的双向边。
- 回到第 \(x\) 次操作后的状态(注意这里的 \(x\) 可以是 0,即回到初始状态)。
- 查询 \(x\) 所在联通块能到的点中点权第 \(y\) 小的值,如果不存在,那么输出 -1。
\(1 \leq n, m \leq 10^5, 0 \leq a_i \leq 10^9\)
zx2003 的论文题,复杂度是 \(O(m\sqrt n)\) 时间,\(O(n)\) 空间,但是跑不过暴力。
先离散化,注意到这里的离散化不需要去重。
建出操作树,然后可以预处理每个操作涉及到的点在当时所属并查集的根(因为要回滚,所以并查集是按秩合并),这个是 \(O(m\log n)\) 的。
值域分块,对于每个询问,我们先求出它属于哪个值域块。
对于每个值域块,用 01 表示某个点是否属于这个值域块,然后用并查集维护每个联通块的和,就可以知道每个联通块里面 1 的个数。注意到并查集的合并是 \(O(1)\) 的,而查询在前面已经预处理过。
所以这步总共是 \(O(m \sqrt n)\)。
然后要求出每个询问的具体答案,需要支持 \(O(1)\) 询问一个点是否和当前的点联通。
将操作树分块,只需要求出关键点。每个关键点处理整张图的连通性,每个操作距离关键点最多只会连 \(O(\sqrt n)\) 条边,将这些边用前向星存下来之后,每次操作bfs一下就可求出当前联通块有哪些点了。
我在实现的时候,为了方便,在关键点处改用了路径压缩并查集。
// Author: HolyK
// Created: Tue Apr 12 00:19:05 2022
#include <bits/stdc++.h>
template <class T, class U>
inline bool smin(T &x, const U &y) {
return y < x ? x = y, 1 : 0;
}
template <class T, class U>
inline bool smax(T &x, const U &y) {
return x < y ? x = y, 1 : 0;
}
using LL = long long;
using PII = std::pair<int, int>;
constexpr int N(1e5 + 5), T(512);
int n, m, a[N], ch[N], g[N], fa[N];
[N];
PII bstruct Option {
int opt, x, y;
} q[N];
bool key[N];
int ans[N], siz[N], bot[N];
int f[N], s[N];
int find(int x) {
while (x != f[x]) x = f[x];
return x;
}
int find2(int x) {
while (x != f[x]) x = f[x] = f[f[x]];
return x;
}
int seq[N * 2], scnt;
void dfs1(int x) {
if (q[x].opt == 1) {
int &u = q[x].x, &v = q[x].y;
= find(u), v = find(v);
u if (u != v) {
[scnt++] = x;
seqif (s[u] > s[v]) std::swap(u, v);
[v] += s[u];
s[u] = v;
f}
} else if (q[x].opt == 3) {
[x].x = find(q[x].x);
q[scnt++] = x;
seq}
for (int i = ch[x], y; y = g[i], i < ch[x + 1]; i++) {
(y);
dfs1}
if (q[x].opt == 1) {
int u = q[x].x, v = q[x].y;
if (u != v) {
[scnt++] = x;
seq[v] -= s[u];
s[u] = u;
f}
}
}
int head[N], to[N], next[N], tot;
void add(int x, int y) {
[++tot] = y, next[tot] = head[x], head[x] = tot;
to}
void del(int x, int y) {
[x] = next[tot--];
head}
int tmp[N];
void dfs3(int x) {
if (key[x]) return;
if (q[x].opt == 1) {
int u = q[x].x, v = q[x].y;
if (u != v) {
(u, v), add(v, u);
add}
} else if (q[x].opt == 3) {
// std::cerr << "! dfs3 " << q[x].x << " " << q[x].y << "\n";
static int que[N], r, vis[N], vcnt;
[r = 1] = q[x].x;
que++;
vcntfor (int i = 1; i <= r; i++) {
int x = que[i];
[x] = vcnt;
visfor (int j = head[x]; j; j = next[j]) {
int y = to[j];
if (vis[y] == vcnt) continue;
[++r] = y;
que}
}
while (ans[x] < n) {
if (vis[find2(b[ans[x]].second)] == vcnt && !q[x].y--) break;
[x]++;
ans}
}
for (int i = ch[x], y; y = g[i], i < ch[x + 1]; i++) {
(y);
dfs3}
if (q[x].opt == 1) {
int u = q[x].x, v = q[x].y;
if (u != v) {
(v, u), del(u, v);
del}
}
}
void work(int x) {
// std::cerr << "key " << x << "\n";
(tmp, f, sizeof f);
memcpyif (q[x].opt == 3) {
while (ans[x] < n) {
if (find2(b[ans[x]].second) == q[x].x && !q[x].y--) break;
[x]++;
ans}
}
for (int i = ch[x], y; y = g[i], i < ch[x + 1]; i++) dfs3(y);
(f, tmp, sizeof f);
memcpy}
void dfs2(int x) {
if (q[x].opt == 1 && q[x].x != q[x].y) f[q[x].x] = q[x].y;
int c = 0;
for (int i = ch[x], y; y = g[i], i < ch[x + 1]; i++) {
(y);
dfs2[x] += siz[y];
sizif (bot[y]) {
++;
c[x] = bot[y];
bot}
}
if (!x || siz[x] + 1 >= T || c > 1) {
[x] = true;
key[x] = x;
bot[x] = 0;
siz(x);
work}
[x]++;
sizif (q[x].opt == 1 && q[x].x != q[x].y) f[q[x].x] = q[x].x;
}
void solve() {
std::cin >> n >> m;
for (int i = 0; i < n; i++) {
std::cin >> a[i];
[i] = {a[i], i};
b}
std::sort(b, b + n);
for (int i = 0; i < n; i++) {
[b[i].second] = i;
a}
for (int i = 1; i <= m; i++) {
int opt, x, y = 0, &f = fa[i];
std::cin >> opt >> x;
if (opt == 2) {
= x;
f } else {
= i - 1;
f std::cin >> y;
--, y--;
x}
[f]++;
ch[i] = {opt, x, y};
q}
for (int i = 1; i <= m; i++) ch[i] += ch[i - 1];
for (int i = 1; i <= m; i++) g[--ch[fa[i]]] = i;
for (int i = 0; i < n; i++) f[i] = i, s[i] = 1;
(0);
dfs1int tot = (n - 1) / T + 1;
for (int i = 0; i < tot; i++) {
for (int j = 0; j < n; j++) {
[j] = a[j] / T == i;
s}
for (int j = 0; j < scnt; j++) {
auto &[opt, x, y] = q[seq[j]];
if (opt == 1) {
if (f[x] == x) {
[x] = y;
f[y] += s[x];
s} else {
[x] = x;
f[y] -= s[x];
s}
} else {
if (ans[seq[j]] / T != i) continue;
if (s[x] <= y) {
-= s[x];
y [seq[j]] += T;
ans}
}
}
}
for (int i = 0; i < n; i++) {
assert(f[i] == i);
}
(0);
dfs2for (int i = 1; i <= m; i++) {
if (q[i].opt == 3) {
std::cout << (ans[i] >= n ? -1 : b[ans[i]].first) << "\n";
}
}
}
int main() {
// freopen("t.in", "r", stdin);
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t = 1;
// std::cin >> t;
while (t--) {
();
solve}
return 0;
}
P4690 [Ynoi2016] 镜中的昆虫
给定序列 \(a_n\),\(m\) 次操作。区间赋值,区间数颜色。
\(1 \leq n, m \leq 10^5, 1 \leq a_i \leq 10^9\)
空间 64MB。
数颜色的套路,数 \(pre < l, l \leq i \leq r\) 的个数,再加一维时间,是三维偏序问题。
用 set 维护区间同色段之后就变成单点修改了,用 CDQ 分治搞一下三维偏序就行,但是这毒瘤空间那么小,我好像搞了多次 CDQ 才卡过,但是后来一次也搞过了,忘了为啥。
时间复杂度 \(O(n\log^2 n)\),空间复杂度 \(O(n)\)。
// Author: HolyK
// Created: Tue Apr 12 15:02:43 2022
#include <bits/stdc++.h>
template <class T, class U>
inline bool smin(T &x, const U &y) {
return y < x ? x = y, 1 : 0;
}
template <class T, class U>
inline bool smax(T &x, const U &y) {
return x < y ? x = y, 1 : 0;
}
using LL = long long;
using PII = std::pair<int, int>;
constexpr int N(1e5 + 5), Q(N * 15);
int n, m, a[N], la, ans[N];
std::map<int, std::set<int>> g;
std::set<int> p;
struct Option {
int x, l, r, id;
bool operator<(const Option &rhs) const {
return x < rhs.x || x == rhs.x && id < rhs.id;
}
} s[Q], t[Q];
int cnt;
namespace Fenwick {
int c[N];
void add(int p, int x) {
for (; p <= n; p += p & -p) {
[p] += x;
c}
}
int ask(int p) {
int r = 0;
for (; p; p -= p & -p) {
+= c[p];
r }
return r;
}
}
int st[Q];
void solve(int l, int r) {
if (l == r) return;
int m = l + r >> 1;
(l, m), solve(m + 1, r);
solveusing namespace Fenwick;
int i = l, j = m + 1;
for (int k = l; k <= r; k++) {
if (j > r || i <= m && s[i] < s[j]) {
if (!s[i].id) {
(s[i].l, s[i].r);
add[++st[0]] = i;
st}
[k] = s[i++];
t} else {
if (s[j].id) {
[s[j].id] += ask(s[j].r) - ask(s[j].l - 1);
ans}
[k] = s[j++];
t}
}
while (st[0]) {
= st[st[0]--];
i (s[i].l, -s[i].r);
add}
for (int k = l; k <= r; k++) s[k] = t[k];
}
void add(int p, int i) {
[++cnt] = {p, i, 1, 0};
s}
void del(int p, int i) {
[++cnt] = {p, i, -1, 0};
s}
void ask(int l, int r, int id) {
[++cnt] = {l - 1, l, r, id};
s}
std::set<int>::iterator split(int x) {
auto u = p.insert(x);
if (u.second) {
int p = *std::prev(u.first);
[x] = a[p];
aauto &v = g[a[x]];
auto it = v.insert(x).first;
(p, x);
addif (++it != v.end()) del(p, *it), add(x, *it);
}
return u.first;
}
void solve() {
std::cin >> n >> m;
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
.insert(i);
p}
.insert(n + 1);
p
for (int i = 1; i <= n; i++) {
auto &v = g[a[i]];
auto it = v.insert(i).first;
int p = it == v.begin() ? 0 : *std::prev(it);
(p, i);
add}
for (int i = 1; i <= m; i++) {
int opt, l, r, x;
std::cin >> opt >> l >> r;
if (opt == 1) {
(++r);
splitfor (auto it = split(l); *it != r; it = p.erase(it)) {
= *it;
x auto &v = g[a[x]];
auto t = v.find(x);
assert(t != v.end());
int p = t == v.begin() ? 0 : *std::prev(t);
= v.erase(t);
t (p, x);
delif (t != v.end()) {
(x, *t);
del(p, *t);
add}
}
std::cin >> x;
auto &v = g[x];
[l] = x;
a.insert(l);
pauto it = v.insert(l).first;
int p = it == v.begin() ? 0 : *std::prev(it);
(p, l);
addif (++it != v.end()) {
(p, *it);
del(l, *it);
add}
} else {
(l);
split(l, r, ++la);
ask}
}
(1, cnt);
solvefor (int i = 1; i <= la; i++) {
std::cout << ans[i] << "\n";
}
}
int main() {
// freopen("t.in", "r", stdin);
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t = 1;
// std::cin >> t;
while (t--) {
();
solve}
return 0;
}
P5073 [Ynoi2015] 世上最幸福的女孩
给定序列 \(a_n\),\(m\) 次操作,全局加,区间求最大子段和。
\(1 \leq n \leq 3\times 10^5, 1 \leq m \leq 6\times 10^5\)。
空间 128MB。
考虑经典线段树做法维护前后缀以及答案。
对于全局加了 \(a\),设原先长度为 \(a\) 的答案为 \(b\),那么新的答案是 \(ax + b\),是经典的维护凸包问题。
对于合并左子树后缀和右子树前缀的情况,可以发现是闵可夫斯基和。
线段树维护一下几个凸包就行。
然后将询问离线,把 \(x\) 从小到大排序,就可以不用在凸包上二分。
但是这样空间炸了。
考虑可以一层一层做,在线段树上从底向上做,每次只存一层的信息即可。
但我还是没卡过去空间,所以经典卡空间方法:考虑将序列分块,每块都重新建线段树算一遍,又好写又快。
实现的时候,把线段树底层改成暴力会有很明显的优化效果。
写这个的时候,维护凸包用的是 kactl 里面那个 lineContainer 的方法,有除法,有点不优,不如直接维护算了。
// Author: HolyK
// Created: Mon Apr 25 10:14:03 2022
#include <bits/stdc++.h>
template <class T, class U>
inline bool smin(T &x, const U &y) {
return y < x ? x = y, 1 : 0;
}
template <class T, class U>
inline bool smax(T &x, const U &y) {
return x < y ? x = y, 1 : 0;
}
using LL = long long;
using PII = std::pair<int, int>;
namespace {
inline char gc() {
static constexpr int BufferSize = 1 << 22 | 5;
static char buf[BufferSize], *p, *q;
static std::streambuf *i = std::cin.rdbuf();
return p == q ? p = buf, q = p + i->sgetn(p, BufferSize), p == q ? EOF : *p++ : *p++;
}
struct Reader {
template <class T>
&operator>>(T &w) {
Reader char c, p = 0;
for (; !std::isdigit(c = gc());) if (c == '-') p = 1;
for (w = c & 15; std::isdigit(c = gc()); w = w * 10 + (c & 15)) ;
if (p) w = -w;
return *this;
}
} cin;
constexpr int N(3e5 + 5), B(N >> 2), T(16);
constexpr LL INF(LLONG_MAX);
struct Line {
int x;
, p;
LL ybool operator<(LL r) const { return p < r; }
(LL z) { return x * z + y; }
LL eval};
using C = std::vector<Line>;
(LL a, LL b) {
LL divreturn a / b - ((a ^ b) < 0 && a % b);
}
void get(Line &a, const Line &b) {
if (a.x == b.x) {
.p = a.y > b.y ? INF : -INF;
a} else {
.p = div(a.y - b.y, b.x - a.x);
a}
}
void add(C &c, int x, LL y) {
= {x, y, INF};
Line o if (c.empty()) {
.push_back(o);
creturn;
}
while (c.size() > 1) {
(c.back(), o);
getif (c[c.size() - 2].p < c.back().p) break;
.pop_back();
c}
(c.back(), o);
get.push_back(o);
c}
int n, m, a[N];
[N];
LL bstruct Node {
, suf, ans;
C pre;
LL sumint pl, sl, al;
} t[B >> 2];
#define ls o << 1
#define rs o << 1 | 1
(const C &a, const C &b) {
C msum= {Line{a[0].x + b[0].x, a[0].y + b[0].y, INF}};
C c for (int i = 0, j = 0; i + 1 < a.size() || j + 1 < b.size(); ) {
&o = c.back();
Line if (j + 1 == b.size() || i + 1 < a.size() && a[i].p < b[j].p) {
(c, o.x + a[i + 1].x - a[i].x, o.y + a[i + 1].y - a[i].y);
add++;
i} else {
(c, o.x + b[j + 1].x - b[j].x, o.y + b[j + 1].y - b[j].y);
add++;
j}
}
return c;
}
(const C &a, const C &b) {
C merge;
C cfor (int i = 0, j = 0; i < a.size() || j < b.size(); ) {
if (j == b.size() || i < a.size() && a[i].x < b[j].x) {
(c, a[i].x, a[i].y);
add++;
i} else {
(c, b[j].x, b[j].y);
add++;
j}
}
return c;
}
void build(int o, int l, int r) {
[o].pl = t[o].sl = t[o].al = 0;
tif (r - l + 1 <= T) {
[o].pre.clear();
t[o].suf.clear();
t[o].ans.clear();
t= 0;
LL s (t[o].pre, 0, 0);
addfor (int i = 1; i <= r - l + 1; i++) {
+= a[l + i - 1];
s (t[o].pre, i, s);
add}
= 0;
s (t[o].suf, 0, 0);
addfor (int i = 1; i <= r - l + 1; i++) {
+= a[r - i + 1];
s (t[o].suf, i, s);
add}
(t[o].ans, 0, 0);
addfor (int i = 1; i <= r - l + 1; i++) {
= -INF;
LL max for (int j = l + i - 1; j <= r; j++) {
(max, b[j] - b[j - i]);
smax}
(t[o].ans, i, max);
add}
[o].sum = s;
treturn;
}
int m = l + r >> 1;
(ls, l, m);
build(rs, m + 1, r);
build
[o].pre = t[ls].pre;
tfor (auto &[x, y, _] : t[rs].pre) {
(t[o].pre, x + m - l + 1, y + t[ls].sum);
add}
[o].suf = t[rs].suf;
tfor (auto &[x, y, _] : t[ls].suf) {
(t[o].suf, x + r - m, y + t[rs].sum);
add}
[o].ans = merge(merge(t[ls].ans, t[rs].ans), msum(t[ls].suf, t[rs].pre));
t[o].sum = t[ls].sum + t[rs].sum;
t}
struct Info {
, a;
LL rvoid apply(LL pre, LL suf, LL ans, LL sum) {
(a, ans);
smax(a, r + pre);
smax= std::max(suf, r + sum);
r }
} qans[N * 2];
(const C &c, LL x) {
LL askauto l = std::lower_bound(c.begin(), c.end(), x);
assert(l != c.end());
return l->x * x + l->y;
}
void ask(int o, int l, int r, int x, int y, LL z, int id) {
if (x <= l && r <= y) {
auto &pl = t[o].pl, &sl = t[o].sl, &al = t[o].al;
while (t[o].pre[pl].p < z) pl++;
while (t[o].suf[sl].p < z) sl++;
while (t[o].ans[al].p < z) al++;
[id].apply(t[o].pre[pl].eval(z), t[o].suf[sl].eval(z), t[o].ans[al].eval(z), t[o].sum + z * (r - l + 1));
qansreturn;
}
if (r - l + 1 <= T) {
, suf, ans, sum, max = 0;
LL pre= suf = ans = sum = 0;
pre for (int i = std::max(x, l); i <= r && i <= y; i++) {
+= a[i] + z;
sum (pre, sum);
smax(ans, sum + max);
smax(max, -sum);
smax}
= 0;
sum for (int i = std::min(r, y); i >= l && i >= x; i--) {
+= a[i] + z;
sum (suf, sum);
smax}
[id].apply(pre, suf, ans, sum);
qansreturn;
}
int m = l + r >> 1;
if (x <= m) ask(ls, l, m, x, y, z, id);
if (y > m) ask(rs, m + 1, r, x, y, z, id);
}
struct Qry {
int l, r, id;
;
LL xbool operator<(const Qry &rhs) const {
return x < rhs.x;
}
} q[N * 2];
int qq;
void solve() {
>> n >> m;
cin for (int i = 1; i <= n; i++) cin >> a[i], b[i] = a[i] + b[i - 1];
= 0;
LL d while (m--) {
int opt, l, r;
>> opt;
cin if (opt == 1) {
>> l;
cin += l;
d } else {
>> l >> r;
cin [qq] = {l, r, qq, d};
q++;
qq}
}
std::sort(q, q + qq);
for (int l = 1, r; l <= n; l += B) {
= std::min(l + B - 1, n);
r (1, l, r);
buildfor (int i = 0; i < qq; i++) {
if (q[i].r < l || q[i].l > r) continue;
(1, l, r, q[i].l, q[i].r, q[i].x, q[i].id);
ask}
}
for (int i = 0; i < qq; i++) {
std::cout << qans[i].a << "\n";
}
}
}
int main() {
// freopen("t.in", "r", stdin);
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t = 1;
// std::cin >> t;
while (t--) {
();
solve}
return 0;
}
P4118 [Ynoi2018] 末日时在做什么?有没有空?可以来拯救吗?
给定序列 \(a_n\),\(m\) 次操作,区间加,区间求最大子段和。
\(1 \leq n, m \leq 10^5\)。
空间 64 MB。
上面那题的进阶版本,1秒时限太毒瘤了,卡不过去,所以这题是 80 分。
顺着上题思路,分块之后每块处理。
修改和询问会变成 \(O(m\sqrt n)\) 次全局操作和 \(O(m)\) 次区间操作。
对于全局加全局查询,按照上题的做法,把询问按照 \(x\) 基数排序,每次做凸包指针移动次数是 \(O(\sqrt n)\)。
对于区间修改,会使得把全局查询的凸包指针失效,但是只会修改 \(O(m)\) 次,所以每次修改之后重置指针,复杂度是 \(O(m\sqrt n)\)。
区间修改,在线段树上更新凸包,这样做的复杂度是 \(O(\sqrt n+ \sqrt n/2+ \sqrt n/4+\dots) = O(\sqrt n)\),所以每次直接在线段树上搞,复杂度是 \(O(m\sqrt n)\)。
区间查询,每次查询可以是 \(O(\sqrt n)\) 的,但是这样写可能常数比较大,我写的是在线段树上查询,每个凸包二分一下,复杂度是 \(O(m \log^2 n)\)。
线段树底层仍然要暴力。
对于信息合并,直接归并的复杂度太高了,观察这个凸包,发现 \(x\) 坐标的范围是 \(1 \dots len\),所以说可以类似桶排序,对于每个 \(x\) 坐标存一下 \(y\) 坐标的最大值,这样 cache 友好,卡常效果明显。
这个信息,本来是用 vector 存,后面卡常把 vector 扔掉了,最后还是没卡过去,真是尽力了。
// Author: HolyK
// Created: Wed Apr 27 14:32:40 2022
#include <bits/stdc++.h>
inline char gc() {
static constexpr int BufferSize = 1 << 22 | 5;
static char buf[BufferSize], *p, *q;
static std::streambuf *i = std::cin.rdbuf();
return p == q ? p = buf, q = p + i->sgetn(p, BufferSize), p == q ? EOF : *p++ : *p++;
}
struct Reader {
template <class T>
&operator>>(T &w) {
Reader char c, p = 0;
for (; !std::isdigit(c = gc());) if (c == '-') p = 1;
for (w = c & 15; std::isdigit(c = gc()); w = w * 10 + (c & 15)) ;
if (p) w = -w;
return *this;
}
} cin;
template <class T, class U>
inline bool smin(T &x, const U &y) {
return y < x ? x = y, 1 : 0;
}
template <class T, class U>
inline bool smax(T &x, const U &y) {
return x < y ? x = y, 1 : 0;
}
using LL = long long;
using PII = std::pair<int, int>;
constexpr int N(1e5 + 5), T(8), B(T << 6);
constexpr LL INF(LLONG_MAX / 3);
struct Pt {
int x;
;
LL yoperator+(const Pt &r) const { return {x + r.x, y + r.y}; }
Pt operator-(const Pt &r) const { return {x - r.x, y - r.y}; }
Pt operator*(const Pt &r) const { return x * r.y - y * r.x; }
LL (const Pt &a, const Pt &b) const { return (a - *this) * (b - *this); }
LL cross(LL z) const { return x * z + y; }
LL eval} pt[B * 30], s[B + 5];
int ptr;
struct Hull {
int l, r;
*begin() const { return pt + l; }
Pt *data() const { return begin(); }
Pt *end() const { return pt + r; }
Pt int size() const { return r - l; }
void assign(Pt *s, Pt *e) {
= l + (e - s);
r (pt + l, s, (e - s) * sizeof(*s));
memcpy}
&operator[](const int &i) const {
Pt // assert(0 <= i && i < size());
return pt[l + i];
}
};
using C = Hull;
// using C = std::vector<Pt>;
void fwd(const C &c, int &p, LL x) {
while (p + 1 < c.size() && c[p].y - c[p + 1].y < x * (c[p + 1].x - c[p].x)) p++;
}
int top;
void add(Pt x) {
while (top > 1 && s[top - 2].cross(s[top - 1], x) >= 0) top--;
[top++] = x;
s}
[B + 5];
LL valvoid update(const Pt &x) { smax(val[x.x], x.y); }
void cal(int n) {
= 0;
top for (int i = 1; i <= n; i++) if (val[i] != -INF) add({i, val[i]});
}
struct Node {
, suf, ans;
C pre;
LL sum} t[B << 2];
[B << 2];
LL tag#define ls o << 1
#define rs o << 1 | 1
void pushup(int o, int len) {
int ll = len - len / 2, rr = len / 2, tmp = ptr;
= t[ls].sum + tag[ls] * ll, r = t[rs].sum + tag[rs] * rr;
LL l , rpre;
C lsuf.l = ptr -= t[ls].suf.size();
lsuf.assign(t[ls].suf.begin(), t[ls].suf.end());
lsuf.l = ptr -= t[rs].pre.size();
rpre.assign(t[rs].pre.begin(), t[rs].pre.end());
rpre// auto lsuf = t[ls].suf, rpre = t[rs].pre;
// pre
= t[ls].pre.size();
top (s, t[ls].pre.data(), top * sizeof(*s));
memcpyfor (int i = 0; i < top; i++) s[i].y += s[i].x * tag[ls];
int cur = top;
for (auto &[x, y] : rpre) {
+= x * tag[rs];
y [cur++] = {x + ll, y + l};
s}
for (int i = top; i < cur; i++) add(s[i]);
[o].pre.assign(s, s + top);
t
// suf
= t[rs].suf.size();
top (s, t[rs].suf.data(), top * sizeof(*s));
memcpyfor (int i = 0; i < top; i++) s[i].y += s[i].x * tag[rs];
= top;
cur for (auto &[x, y] : lsuf) {
+= x * tag[ls];
y [cur++] = {x + rr, y + r};
s}
for (int i = top; i < cur; i++) add(s[i]);
[o].suf.assign(s, s + top);
t
// ans: lr
for (int i = 1; i <= len; i++) val[i] = -INF;
for (auto &[x, y] : t[ls].ans) smax(val[x], y + x * tag[ls]);
for (auto &[x, y] : t[rs].ans) smax(val[x], y + x * tag[rs]);
// ans: merge
(lsuf[0] + rpre[0]);
updatefor (int i = 0, j = 0; i + 1 < lsuf.size() || j + 1 < rpre.size(); ) {
if (j + 1 == rpre.size() || i + 1 < lsuf.size() && (lsuf[i + 1] - lsuf[i]) * (rpre[j + 1] - rpre[j]) < 0) {
++;
i} else {
++;
j}
(lsuf[i] + rpre[j]);
update}
(len);
cal[o].ans.assign(s, s + top);
t
[o].sum = l + r;
t
= tmp;
ptr }
[N];
LL avoid init(int o, int l, int r) {
= 0;
LL sum = 0;
top for (int i = l; i <= r; i++) {
+= a[i];
sum ({i - l + 1, sum});
add}
[o].pre.assign(s, s + top);
t= 0, sum = 0;
top for (int i = r; i >= l; i--) {
+= a[i];
sum ({r - i + 1, sum});
add}
[o].suf.assign(s, s + top);
t[o].sum = sum;
tfor (int i = 1; i <= r - l + 1; i++) val[i] = -INF;
for (int i = l; i <= r; i++) {
= 0;
sum for (int j = i; j <= r; j++) {
+= a[j];
sum (val[j - i + 1], sum);
smax}
}
(r - l + 1);
cal[o].ans.assign(s, s + top);
t}
void build(int o, int l, int r) {
[o].pre.l = ptr -= r - l + 1;
t[o].suf.l = ptr -= r - l + 1;
t[o].ans.l = ptr -= r - l + 1;
t[o] = 0;
tagif (r - l + 1 <= T) {
(o, l, r);
initreturn;
}
int m = l + r >> 1;
(ls, l, m);
build(rs, m + 1, r);
build(o, r - l + 1);
pushup}
void update(int o, int l, int r, int x, int y, LL z) {
if (x <= l && r <= y) {
[o] += z;
tagreturn;
}
if (r - l + 1 <= T) {
// std::cerr << "updateI " << l << " " << r << " " << x << " " << y << "\n";
for (int i = std::max(l, x); i <= r && i <= y; i++) a[i] += z;
(o, l, r);
initreturn;
}
int m = l + r >> 1;
if (x <= m) update(ls, l, m, x, y, z);
if (y > m) update(rs, m + 1, r, x, y, z);
(o, r - l + 1);
pushup}
struct Info {
, a;
LL rvoid apply(LL pre, LL suf, LL ans, LL sum) {
(a, ans);
smax(a, r + pre);
smax= std::max(suf, r + sum);
r }
} qans[N];
(const C &c, LL x) {
LL askint l = 0, r = c.size() - 1;
while (l < r) {
int m = l + r >> 1;
if (c[m].y - c[m + 1].y < x * (c[m + 1].x - c[m].x)) {
= m + 1;
l } else {
= m;
r }
}
return c[l].eval(x);
}
void ask(int o, int l, int r, int x, int y, int id, LL z) {
+= tag[o];
z if (x <= l && r <= y) {
[id].apply(ask(t[o].pre, z), ask(t[o].suf, z), ask(t[o].ans, z), t[o].sum + z * (r - l + 1));
qansreturn;
}
if (r - l + 1 <= T) {
, suf, ans, sum, max;
LL pre= suf = ans = sum = max = 0;
pre for (int i = std::max(x, l); i <= r && i <= y; i++) {
+= a[i] + z;
sum (pre, sum);
smax(ans, sum + max);
smax(max, -sum);
smax}
= 0;
sum for (int i = std::min(r, y); i >= l && i >= x; i--) {
+= a[i] + z;
sum (suf, sum);
smax}
[id].apply(pre, suf, ans, sum);
qansreturn;
}
int m = l + r >> 1;
if (x <= m) ask(ls, l, m, x, y, id, z);
if (y > m) ask(rs, m + 1, r, x, y, id, z);
}
struct Operation {
int opt, l, r, x;
} op[N];
struct Qry {
;
LL xint id;
} q[N], nq[N];
void solve() {
int n, m;
>> n >> m;
cin for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 0; i < m; i++) {
int opt, l, r, x = 0;
>> opt >> l >> r;
cin if (opt == 1) cin >> x;
[i] = {opt, l, r, x};
op}
for (int l = 1; l <= n; l += B) {
= B * 30;
ptr int r = std::min(l + B - 1, n);
(1, l, r);
buildint pl, sl, al, qn;
auto init = [&] { pl = sl = al = qn = 0; };
auto run = [&] {
if (!qn) return;
if (qn > 800) {
= LLONG_MAX;
LL min for (int i = 0; i < qn; i++) smin(min, q[i].x);
for (int i = 0; i < qn; i++) q[i].x -= min;
static int c[256];
#define SORT(a, b, n, d) \
memset(c, 0, sizeof c); \
for (int i = 0; i < n; i++) c[a[i].x >> d & 0xff]++; \
for (int i = 1; i < 256; i++) c[i] += c[i - 1]; \
for (int i = qn - 1; i >= 0; i--) b[--c[a[i].x >> d & 0xff]] = a[i];
(q, nq, qn, 0);
SORT(nq, q, qn, 8);
SORT(q, nq, qn, 16);
SORT(nq, q, qn, 24);
SORT
for (int i = 0; i < qn; i++) q[i].x += min;
} else {
std::sort(q, q + qn, [&](const Qry &i, const Qry &j) { return i.x < j.x; });
}
for (int i = 0; i < qn; i++) {
(t[1].pre, pl, q[i].x);
fwd(t[1].suf, sl, q[i].x);
fwd(t[1].ans, al, q[i].x);
fwd[q[i].id].apply(t[1].pre[pl].eval(q[i].x), t[1].suf[sl].eval(q[i].x), t[1].ans[al].eval(q[i].x), t[1].sum + (r - l + 1) * q[i].x);
qans}
};
();
initfor (int i = 0; i < m; i++) {
if (op[i].r < l || op[i].l > r) continue;
if (op[i].l <= l && r <= op[i].r) {
if (op[i].opt == 1) {
[1] += op[i].x;
tag} else {
[qn++] = {tag[1], i};
q}
} else {
if (op[i].opt == 1) {
();
run();
init(1, l, r, op[i].l, op[i].r, op[i].x);
update} else {
(1, l, r, op[i].l, op[i].r, i, 0);
ask}
}
}
();
run}
for (int i = 0; i < m; i++) {
if (op[i].opt == 2) {
std::cout << qans[i].a << "\n";
}
}
}
int main() {
// freopen("t.in", "r", stdin);
// freopen("t.out", "w", stdout);
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t = 1;
// std::cin >> t;
while (t--) {
();
solve}
std::cerr << (double)clock() / CLOCKS_PER_SEC << "\n";
return 0;
}
P6783 [Ynoi2008] rrusq
给定 \(n\) 个二维平面上的点,每个点有权值,给定 \(m\) 个矩形,\(q\) 次询问第 \(l\) 到 第 \(r\) 个矩形的并覆盖的点的权值之和。
\(n, m \leq 10^5, q \leq 10^6\)
考虑一维的情况,离线之后扫描右端点,维护左端点的答案,显然每次进行区间染色,查询颜色 \(\geq l\) 的区间权值之和即可。
拓展到二维,用 leafy-KD-tree 将二维矩形变成 \(O(\sqrt n)\) 个树上节点,染色时暴力回收区间的染色标记,标记需要pushdown。
这个回收操作是染色操作的逆操作,所以操作总次数是 \(O(m\sqrt n)\),需要搞一个 \(O(x)-O(xn^{\frac1x})\) 的数据结构来维护。
// Author: HolyK
// Created: Mon Sep 19 10:23:32 2022
#include <algorithm>
#include <bits/stdc++.h>
template <class T, class U>
inline bool smin(T &x, const U &y) {
return y < x ? x = y, 1 : 0;
}
template <class T, class U>
inline bool smax(T &x, const U &y) {
return x < y ? x = y, 1 : 0;
}
using LL = long long;
using PII = std::pair<int, int>;
constexpr int N(1e5 + 5), L(6), MASK((1 << L) - 1), NL(N >> L * 2);
struct SumDS {
int s[3][N + (1 << L)];
void add(int p, int x) {
[0][p] += x;
s[1][p >>= L] += x;
s[2][p >>= L] += x;
s}
int ask(int p) {
int ans = 0;
while (p & MASK) ans += s[0][p++];
>>= L;
p while (p & MASK) ans += s[1][p++];
>>= L;
p while (p <= NL) ans += s[2][p++];
return ans;
}
} sumDS;
int n, m, a[N][3];
#define ls o << 1
#define rs o << 1 | 1
struct Node {
int l[2], r[2], sum, col, vis;
} t[N << 2];
int p[N];
template <int D>
void build(int o, int l, int r) {
[o].col = -1;
t[o].vis = 0;
tif (l == r) {
[o].sum = a[p[l]][2];
t[o].l[0] = t[o].r[0] = a[p[l]][0];
t[o].l[1] = t[o].r[1] = a[p[l]][1];
treturn;
}
int m = l + r >> 1;
std::nth_element(p + l, p + m, p + r + 1,
[&](int i, int j) { return a[i][D] < a[j][D]; });
<D ^ 1>(ls, l, m);
build<D ^ 1>(rs, m + 1, r);
build[o].sum = t[ls].sum + t[rs].sum;
t
[o].l[0] = std::min(t[ls].l[0], t[rs].l[0]);
t[o].l[1] = std::min(t[ls].l[1], t[rs].l[1]);
t[o].r[0] = std::max(t[ls].r[0], t[rs].r[0]);
t[o].r[1] = std::max(t[ls].r[1], t[rs].r[1]);
t}
void pushdown(int o) {
if (t[o].col != -1) {
[ls].col = t[rs].col = t[o].col;
t[ls].vis = t[rs].vis = 1;
t[o].col = -1;
t}
}
void clear(int o) {
if ((o < N * 4) && t[o].vis) {
[o].vis = 0;
tif (t[o].col != -1) {
.add(t[o].col, -t[o].sum);
sumDS[o].col = -1;
t}
(ls), clear(rs);
clear}
}
void update(int o, int l, int r, const std::array<int, 4> &x, int y) {
if (t[o].r[0] < x[0] || x[1] < t[o].l[0] || t[o].r[1] < x[2] || x[3] < t[o].l[1]) return;
if (t[o].l[0] >= x[0] && x[1] >= t[o].r[0] && t[o].l[1] >= x[2] && x[3] >= t[o].r[1]) {
(o);
clear[o].col = y;
t[o].vis = 1;
t.add(y, t[o].sum);
sumDSreturn;
}
assert(l < r);
(o);
pushdown
int m = l + r >> 1;
(ls, l, m, x, y);
update(rs, m + 1, r, x, y);
update[o].vis = 1;
t}
void solve() {
std::cin >> n;
for (int i = 1; i <= n; i++) {
[i][0] = i;
astd::cin >> a[i][1] >> a[i][2];
[i] = i;
p}
<0>(1, 1, n);
build
std::cin >> m;
std::vector<std::array<int, 4>> b(m);
for (auto &v : b) std::cin >> v[0] >> v[1] >> v[2] >> v[3];
std::cin >> m;
std::vector<std::array<int, 3>> q(m);
for (int i = 0, l, r; i < m; i++) {
std::cin >> l >> r;
[i] = {r, l - 1, i};
q}
std::sort(q.begin(), q.end());
std::vector<int> ans(m);
int i = 0;
for (auto &[r, l, id] : q) {
while (i < r) update(1, 1, n, b[i], i), i++;
[id] = sumDS.ask(l);
ans}
for (auto &z : ans) std::cout << z << "\n";
}
int main() {
// freopen("t.in", "r", stdin);
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t = 1;
// std::cin >> t;
while (t--) {
();
solve}
return 0;
}
P6109 [Ynoi2009] rprmq1
\(n\times n\) 平面,先做 \(m\) 次矩形加,再 \(q\) 次矩形求最大值
\(n, m\leq 50000, q \leq 5\times 10^5\)
转换成查询版本区间 \([l_1, r_1]\) 在区间 \([l_2, r_2]\) 的最大值。
如果是求和,那是可减的,写个主席树减一下就行了。
但是求最大值,没办法减,考虑给他拆成一个前缀和一个后缀的答案。
分治,每次处理跨区间中点的询问。
然后就是一个线段树维护历史最大值,有一些细节,在处理前缀集合 \([mid, i]\) 之前,需要把历史最大值清空,然后每次把所有右端点为 \(i\) 的操作全部加完之后再贡献到历史最大值上面,不然先加后减答案会错。
这个分治操作可以用位运算转成非递归的。
constexpr LL NINF = -1e18;
struct Tag {
= 0, b = NINF, c = 0;
LL a void apply(const Tag &r) {
= std::max(b + r.c, a + r.b);
b += r.a, c += r.c;
a (b, NINF);
smax(c, NINF);
smax}
};
struct Info {
= 0, hmax = 0;
LL max void apply(const Tag &r) {
= std::max(max + r.b, hmax + r.c);
hmax += r.a;
max }
operator+(const Info &r) const {
Info return {std::max(max, r.max), std::max(hmax, r.hmax)};
}
};
using SegTree = LazySegmentTree<Info, Tag>;
void solve() {
int n, m, q;
>> n >> m >> q;
cin std::vector<int> co(n + 1);
std::vector<std::array<int, 3>> op(m * 2);
{
std::vector<std::array<int, 5>> t(m);
for (int i = 0; i < m; i++) {
int l1, l2, r1, r2, x;
>> l1 >> l2 >> r1 >> r2 >> x;
cin [i] = {--l1, --l2, r1, r2, x};
t[l1]++, co[r1]++;
co}
for (int i = 1; i <= n; i++) co[i] += co[i - 1];
for (auto &[l1, l2, r1, r2, x] : t) {
[--co[l1]] = {l2, r2, x};
op[--co[r1]] = {l2, r2, -x};
op}
}
std::vector<std::array<int, 5>> qry(q);
std::vector<int> cq(n + 1), seq(q);
for (int i = 0; i < q; i++) {
int k, l1, l2, r1, r2;
>> l1 >> l2 >> r1 >> r2;
cin --, l2--, r1--;
l1if (l1 == r1) {
= r1;
k } else {
= 1 << std::__lg(l1 ^ r1);
k = r1 & -k;
k }
[i] = {k, l1, r1, l2, r2};
qry
// std::cerr << l1 << " " << r1 << " " << l2 << " " << r2 << " " << k << "\n";
[k]++;
cq}
std::vector<LL> ans(q);
// R
std::vector<int> p(q);
std::iota(p.begin(), p.end(), 0);
std::sort(p.begin(), p.end(), [&](int i, int j) {
return qry[i][2] > qry[j][2];
});
for (int i = 1; i <= n; i++) cq[i] += cq[i - 1];
for (int i : p) seq[--cq[qry[i][0]]] = i;
(n);
SegTree segfor (int o = 0; o < n; o++) {
int r = o ? std::min(o + (o & -o), n) : 1;
int k = cq[o];
for (int i = o; i < r; i++) {
for (int j = co[i]; j < co[i + 1]; j++) {
.rangeApply(op[j][0], op[j][1], {op[j][2], NINF, 0});
seg}
.rangeApply(0, n, {0, 0, 0});
segwhile (k < cq[o + 1] && qry[seq[k]][2] == i) {
int l = qry[seq[k]][3], r = qry[seq[k]][4];
(ans[seq[k++]], seg.rangeQuery(l, r).hmax);
smax}
}
assert(k == cq[o + 1]);
for (int j = co[o + 1]; j < co[r]; j++) {
.rangeApply(op[j][0], op[j][1], {-op[j][2], 0, 0});
seg}
.rangeApply(0, n, {0, NINF, NINF});
seg}
// L
std::sort(p.begin(), p.end(), [&](int i, int j) {
return qry[i][1] < qry[j][1];
});
for (int i = 1; i <= n; i++) cq[i - 1] = cq[i];
for (int i : p) seq[--cq[qry[i][0]]] = i;
for (int o = n - 1; o > 0; o--) {
int l = o & o - 1;
int k = cq[o];
while (k < cq[o + 1] && qry[seq[k]][1] == o) {
++;
k}
for (int i = o - 1; i >= l; i--) {
for (int j = co[i + 1]; j < co[i + 2]; j++) {
.rangeApply(op[j][0], op[j][1], {-op[j][2], NINF, 0});
seg}
.rangeApply(0, n, {0, 0, 0});
seg
while (k < cq[o + 1] && qry[seq[k]][1] == i) {
int l = qry[seq[k]][3], r = qry[seq[k]][4];
(ans[seq[k++]], seg.rangeQuery(l, r).hmax);
smax}
}
assert(k == cq[o + 1]);
for (int j = co[l + 1]; j < co[o]; j++) {
.rangeApply(op[j][0], op[j][1], {op[j][2], 0, 0});
seg}
.rangeApply(0, n, {0, NINF, NINF});
seg}
for (auto &z : ans) cout << z << "\n";
}
P8211 [THUPC2022 初赛] 搬砖
有 \(n\) 摞砖,你有精力值 \(d\)。每小时,你会搬走每一摞砖最上面的 \(d\) 块(不足则全部搬走),一摞砖有 \(a\) 块,还有属性 \(b\),搬完这一摞砖后,精力值就会下降 \(b\)。如果没有任何一摞砖被搬完或者精力值下降到 \(0\) 或以下,你就会停止工作。如果你发现自己需要工作但是所有的砖已被搬完,这一小时仍算作工作时间。有 \(T\) 次操作,每次操作加一摞砖,给出精力值 \(d\) 并询问工作时间。
\(T\le 351493,1\le op\le 2,1\le a\le 100000,0\le b\le 100000,1\le d \le 100000\)。
设 \(x\) 表示当前精力值。
如何判断没有任何一摞砖被搬完:维护一个 \(now\) 表示已经搬了的砖的数量,只需要检查 \((now, now+x]\) 内有没有砖即可。
如果 \(b > 0\) 则答案是根号级别: 令 \(B = \sqrt{N}\) (\(N=1000000\)),如果 \(x \geq B\),那么最多搬 \(N / B\) 轮。
如果 \(x < B\),发现每轮 \(x\) 至少减 1,最多 \(x\) 轮结束。
但是有 \(b = 0\) 的情况,这会导致一轮下来可能 \(x\) 没变。 这样的话,\(now\) 会一直 += \(x\),考虑对每个 \(x < B\) 开一个并查集,\(j \to j+x\) 当且仅当 \((j, j + x]\) 有砖块。每轮搞之前先拿并查集跳一下。
对于并查集,如果每次暴力维护则是 \(O(n)\) 的,但是显然可以用一些简单的操作让每个 \(j\to j+x\) 的边只会被考虑 \(O(1)\) 次。
需要搞一堆 \(O(\sqrt N)\) - \(O(1)\) 的数据结构。
constexpr int N(1e5 + 5), T(256), S(N / T + 1);
template <class Tp>
struct PSumDS {
[N], s1[S];
Tp s0void add(int x, Tp y) {
int k = x / T;
for (int i = x; i < k * T + T && i < N; i++) s0[i] += y;
for (int i = k + 1; i < S; i++) s1[i] += y;
}
(int x) {
Tp ask(x, 1e5);
sminreturn s0[x] + s1[x / T];
}
(int l, int r) {
Tp askreturn ask(r) - ask(l);
}
};
struct SMinDS {
int m0[N], m1[S];
() {
SMinDS(m0, 0x3f, sizeof m0);
memset(m1, 0x3f, sizeof m1);
memset}
void add(int x, int y) {
int k = x / T;
for (int i = k * T; i <= x; i++) smin(m0[i], y);
for (int i = 0; i < k; i++) smin(m1[i], y);
}
int ask(int x) {
return std::min(m0[x], m1[x / T]);
}
};
struct DSU {
int f[N + T];
void init(int n) {
for (int i = 0; i <= n; i++) f[i] = i;
}
int find(int x) {
while (x != f[x]) x = f[x] = f[f[x]];
return x;
}
};
<int> cnt;
PSumDS<LL> sum;
PSumDS;
SMinDS next[T];
DSU dvoid solve() {
for (int i = 1; i < T; i++) {
[i].init(1e5 + T);
d}
int t;
std::cin >> t;
while (t--) {
int opt;
std::cin >> opt;
if (opt == 1) {
int x, y;
std::cin >> x >> y;
.add(x, 1);
cnt.add(x, y);
sumif (y) next.add(x, x);
int k = x;
while (k < x + T && cnt.ask(k, k + 1) == 0) k++;
for (int i = 1; i < T; i++) {
for (int j = std::min(x - 1, k - i); j >= 0 && j >= x - i && d[i].f[j] == j; j--) {
[i].f[j] = j + i;
d}
}
} else {
;
LL xstd::cin >> x;
int ans = 0, now = 0;
while (x > 0) {
++;
ansif (cnt.ask(now, now + x) == 0) break;
if (x < T) {
int y = std::min(next.ask(now + 1), d[x].find(now));
int k = (y - 1 - now) / x;
+= k;
ans assert(sum.ask(now, now + x * k) == 0);
+= x * k;
now }
+= x;
now -= sum.ask(now - x, now);
x }
std::cout << ans << "\n";
}
}
}
Just Another Data Structure Problem
给定 \(n,m\),以及序列 \(a_{1}, a_{2}, \cdots, a_{n}\) 和 \(1,2,\cdots ,n\) 的排列 \(y_{1},y_{2},\cdots,y_{n}\),你需要回答 \(m\) 个询问。对每个询问, 给定 \(l,r\), 查询: \(\sum_{i=1}^{n}\sum_{j=i+1}^{n}[a_{i}=a_{j}]\cdot\prod_{k=i}^{j}[l\leq y_{k}\leq r]\)
\(n \leq 10^5, m \leq 10^6\)
仔细看一下这个式子,可以发现是求,对于每个 \(l \leq y \leq r\) 的连续段,求 \(\sum \binom{c_i}{2}\)
然后看一下这个 \(m\) 的范围,肯定是经典的 \(O(x)\) - \(O(xn^{1/x})\) 的多层分块了。
对于每一种 \(a\),分别计算,设 \(a_i = x\) 的出现位置 \(i\) 为 \(p_1, p_2, \dots, p_k\)。
设 \(L_i = \min\{a[p_i:p_{i+1}]\}, R_i = \max\{a[p_i:p_{i+1}]\}\),那合法要求是 \(l \leq L_i, R_i \leq r\)。
下面按照 \(k\) 的大小进行根号分治。令 \(B = 2000\)。
如果 \(k < B\),考虑暴力,把每个 \([1, k]\) 的子区间的 \(\min L_i, \max R_i\) 贡献到答案。这个子区间的总数是 \(\sum k^2 = O(nB)\)。
用单调栈(即笛卡尔树)处理每个 \(L\) 对应的区间,最后扫描线求答案,这部分的复杂度是 \(O(xnB + mn^{1/x})\)。
如果 \(k \geq B\),把 \(L, R\) 离散化,然后是经典的用链表维护连续段的问题,可以用回滚莫队解决。这部分的复杂度是 \(O(\sum k\sqrt m) = O(n\sqrt m + m \frac{n}{B})\)
总结:如果我们需要做询问次数为 \(m\) 的若干次莫队,莫队序列总长度为 \(n\),则可以根号分治,小的用其他算法处理,大的直接莫队。
constexpr int N(1e5 + 5), M(1e6 + 5);
struct SumDS {
int s0[N], s1[N], s2[N];
void add(int p, int x) {
[p] += x;
s0[p >> 5] += x;
s1[p >> 10] += x;
s2}
int ask(int p) {
int r = 0, b2 = p >> 10, b1 = p >> 5;
for (int i = 0; i < b2; i++) r += s2[i];
for (int i = b2 << 5; i < b1; i++) r += s1[i];
for (int i = b1 << 5; i <= p; i++) r += s0[i];
return r;
}
} sumDS;
int n, m, a[N], b[N], min[17][N], max[17][N], lg[N];
std::vector<int> g[N];
struct Qry {
int l, r, id;
} lq[M], rq[M];
int ql[M], qr[M];
[M];
LL ansstruct SuccDS {
char c[N];
int l[N], r[N];
;
LL sumvoid link(int x) {
+= (x - l[x] + 1LL) * (r[x + 1] - x);
sum [l[x]] = r[x + 1], l[r[x + 1]] = l[x];
r}
void cut(int x) {
[l[x]] = x, l[r[x + 1]] = x + 1;
r}
void add(int i) {
if (++c[i] == 2) {
[i] = r[i] = i;
l++;
sumif (i && c[i - 1] == 2) link(i - 1);
if (c[i + 1] == 2) link(i);
}
}
void del(int i) {
if (c[i] == 2) {
if (i && c[i - 1] == 2) cut(i - 1);
if (c[i + 1] == 2) cut(i);
}
[i]--;
c}
void clear(int k) {
(c, 0, k);
memset= 0;
sum }
} succDS;
void solve(std::vector<PII> &p) {
static constexpr double Alpha = 1.8;
int k = p.size();
int t = 1.0 * k / sqrt(m) * Alpha;
std::vector<int> lp(k), rp(k);
for (int i = 0; i < k; i++) {
[i] = rp[i] = i;
lp}
std::sort(lp.begin(), lp.end(), [&](int i, int j) {
return p[i].first < p[j].first;
});
std::sort(rp.begin(), rp.end(), [&](int i, int j) {
return p[i].second < p[j].second;
});
int ptr = 1;
for (int i = 0; i < k; i++) {
while (ptr <= m && lq[ptr].l <= p[lp[i]].first) ql[lq[ptr++].id] = i;
}
while (ptr <= m) ql[lq[ptr++].id] = k;
= m;
ptr for (int i = k - 1; i >= 0; i--) {
while (ptr && rq[ptr].r >= p[rp[i]].second) qr[rq[ptr--].id] = i;
}
while (ptr) qr[rq[ptr--].id] = -1;
std::vector<int> cnt((k - 1) / t + 1);
std::vector<std::vector<int>> g((k - 1) / t + 1);
for (int i = 1; i <= m; i++) {
int id = lq[m - i + 1].id;
if (ql[id] < k && qr[id] >= 0) cnt[qr[id] / t]++;
}
for (int i = 0; i < g.size(); i++) {
.reserve(cnt[i]);
g}
for (int i = 1; i <= m; i++) {
int id = lq[m - i + 1].id;
if (ql[id] < k && qr[id] >= 0) g[qr[id] / t].push_back(id);
}
for (int i = 0; i < g.size(); i++) {
if (g[i].empty()) continue;
int bound = i * t, j = k - 1;
for (int j = 0; j < bound; j++) succDS.add(rp[j]);
for (auto id : g[i]) {
while (j >= ql[id]) succDS.add(lp[j--]);
= succDS.sum;
LL pre for (int k = bound; k <= qr[id]; k++) succDS.add(rp[k]);
[id] += succDS.sum;
ans.sum = pre;
succDSfor (int k = qr[id]; k >= bound; k--) succDS.del(rp[k]);
}
.clear(k);
succDS}
}
[N], *cur = val;
PII valstruct Node {
*l, *m, *r;
PII };
std::vector<Node> t[N];
void build(PII *l, PII *r) {
if (r - l <= 0) return;
*m = std::min_element(l, r);
PII [m->first].push_back({l, m, r});
t(l, m), build(m + 1, r);
build}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
>> n >> m;
cin for (int i = 1; i <= n; i++) {
>> a[i];
cin [a[i]].push_back(i);
g}
for (int i = 1; i <= n; i++) {
>> b[i];
cin [0][i] = max[0][i] = b[i];
min}
for (int i = 1, l, r; i <= m; i++) {
>> l >> r;
cin [i] = rq[i] = {l, r, i};
lq}
std::sort(lq + 1, lq + 1 + m, [](Qry i, Qry j) { return i.l < j.l; });
std::sort(rq + 1, rq + 1 + m, [](Qry i, Qry j) { return i.r < j.r; });
for (int i = 2; i <= n; i++) {
[i] = lg[i >> 1] + 1;
lg}
for (int i = 1; i < 17; i++) {
for (int j = 1 << i; j <= n; j++) {
[i][j] = std::min(min[i - 1][j], min[i - 1][j - (1 << i - 1)]);
min[i][j] = std::max(max[i - 1][j], max[i - 1][j - (1 << i - 1)]);
max}
}
for (int i = 1; i <= n; i++) {
if (g[i].size() <= 1) continue;
std::vector<PII> p;
for (int j = 1; j < g[i].size(); j++) {
int l = g[i][j - 1], r = g[i][j], k = lg[r - l + 1];
.push_back({
pstd::min(min[k][r], min[k][l + (1 << k) - 1]),
std::max(max[k][r], max[k][l + (1 << k) - 1])
});
}
if (p.size() >= 2048) {
(p);
solve} else {
std::copy(p.begin(), p.end(), cur);
(cur, cur + p.size());
build+= p.size();
cur }
}
for (int i = n, j = m; i && j; i--) {
for (auto &p : t[i]) {
int max = p.m->second;
for (auto l = p.m; l >= p.l; l--) {
(max, l->second);
smaxint now = max;
for (auto r = p.m; r < p.r; r++) {
(now, r->second);
smax.add(now, 1);
sumDS}
}
}
for (; j && lq[j].l == i; j--) {
[lq[j].id] += sumDS.ask(lq[j].r);
ans}
}
for (int i = 1; i <= m; i++) {
std::cout << ans[i] << "\n";
}
return 0;
}
P4689 [Ynoi2016] 这是我自己的发明
\(n\) 个点的树,有点权,初始根是 1,\(m\) 次操作,换根为 \(x\);给两个点 \(x, y\),求从 \(x\) 子树内和 \(y\) 子树中分别选一个点,求两个点点权相等的情况数。
\(1 \leq n \leq 10^5, 1 \leq m \leq 5\times 10^5, 1 \leq a_i \leq 10^9\)
假换根,用dfs序搞一下,显然可以转换成序列问题。然后序列上答案就是 \(\sum \operatorname{cnt}(l_1, r_1, x)\cdot \operatorname{cnt}(l_2, r_2, x)\),拆成两个前缀和相减的形式,可以变成 4 个询问,这个很好用莫队维护。
然后你换根的话会可能会拆成4个询问,但是你发现换根可以拆成前缀或者后缀,那你莫队的时候可以把前缀和后缀答案都维护一下。
constexpr int N(1e5 + 5);
int n, m, a[N], b[N], up[20][N], in[N], out[N], dep[N];
std::vector<int> g[N];
void dfs(int x) {
[x] = ++in[0];
in[in[0]] = a[x];
bfor (int y : g[x]) {
if (y == up[0][x]) continue;
[0][y] = x;
up[y] = dep[x] + 1;
dep(y);
dfs}
[x] = in[0];
out}
int jmp(int x, int y) {
assert(dep[x] > dep[y]);
for (int i = 0; dep[x] - dep[y] > 1; i++) {
if (dep[x] - dep[y] - 1 >> i & 1) x = up[i][x];
}
return x;
}
struct Qry {
int l, r, u, v, id;
bool operator<(const Qry &rhs) const {
return l / 512 == rhs.l / 512 ? r < rhs.r : l < rhs.l;
}
};
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n >> m;
for (int i = 1; i <= n; i++) std::cin >> a[i];
std::vector<int> v(a + 1, a + 1 + n);
std::sort(v.begin(), v.end());
for (int i = 1; i <= n; i++)
[i] = std::lower_bound(v.begin(), v.end(), a[i]) - v.begin();
afor (int i = 1, x, y; i < n; i++) {
std::cin >> x >> y;
[x].push_back(y);
g[y].push_back(x);
g}
(1);
dfsfor (int i = 1; i < 20; i++) {
for (int j = 1; j <= n; j++) {
[i][j] = up[i - 1][up[i - 1][j]];
up}
}
int root = 1;
std::vector<LL> ans(m);
std::vector<Qry> q;
for (int i = 0; i < m; i++) {
int opt, x, y;
std::cin >> opt >> x;
if (opt == 1) {
= x;
root [i] = -1e9;
ans} else {
std::cin >> y;
auto get = [&](int x) -> std::vector<PII> {
if (root == x) return {PII(n, 0)};
if (in[x] <= in[root] && in[root] <= out[x]) {
= jmp(root, x);
x return {PII(in[x] - 1, 0), PII(out[x] + 1, 1)};
} else {
return {PII(-in[x] + 1, 0), PII(out[x], 0)};
}
};
auto vx = get(x), vy = get(y);
for (auto [l, u] : vx) {
int cx = l < 0 ? l = -l, -1 : 1;
if (l < 1 || l > n) continue;
for (auto [r, v] : vy) {
int cy = r < 0 ? r = -r, -1 : 1;
if (r < 1 || r > n) continue;
.push_back({l, r, u, v, (i + 1) * cx * cy});
q}
}
}
}
std::sort(q.begin(), q.end());
int x = 1, y = 1;
std::vector<std::array<int, 2>> cl(n), cr;
[b[1]][0]++;
clfor (int i = 1; i <= n; i++) cl[b[i]][1]++;
[2][2];
LL now= cl;
cr for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
[i][j] = 0;
nowfor (int k = 0; k < n; k++) {
[i][j] += 1LL * cl[k][i] * cr[k][j];
now}
}
}
for (auto [l, r, u, v, id] : q) {
while (x < l) {
[b[x]][1]--;
cl[1][0] -= cr[b[x]][0];
now[1][1] -= cr[b[x]][1];
now[b[++x]][0]++;
cl[0][0] += cr[b[x]][0];
now[0][1] += cr[b[x]][1];
now}
while (x > l) {
[b[x]][0]--;
cl[0][0] -= cr[b[x]][0];
now[0][1] -= cr[b[x]][1];
now[b[--x]][1]++;
cl[1][0] += cr[b[x]][0];
now[1][1] += cr[b[x]][1];
now}
while (y < r) {
[b[y]][1]--;
cr[0][1] -= cl[b[y]][0];
now[1][1] -= cl[b[y]][1];
now[b[++y]][0]++;
cr[0][0] += cl[b[y]][0];
now[1][0] += cl[b[y]][1];
now}
while (y > r) {
[b[y]][0]--;
cr[0][0] -= cl[b[y]][0];
now[1][0] -= cl[b[y]][1];
now[b[--y]][1]++;
cr[0][1] += cl[b[y]][0];
now[1][1] += cl[b[y]][1];
now}
int c = id < 0 ? id = -id, -1 : 1;
[id - 1] += c * now[u][v];
ans// dbg("%d %d %d %d : %lld\n", l, r, u, v, now[u][v]);
}
for (auto x : ans)
if (x != -1e9) std::cout << x << "\n";
return 0;
}
P7897 [Ynoi2006] spxmcq
给定一颗 \(n\) 个节点有根树,第 \(i\) 节点权值为 \(a_i\)。
在这个树上支持一种询问:给定节点 \(u\) 和参数 \(x\),假如 所有节点点权加 \(x\),在这种情况下,求对于所有完全在 \(u\) 子树内并包含 \(u\) 的连通点集,权值之和最大可能为多少?
\(1\le n,m\le 10^6\),\(|a_i|,|x|\le 10^8\)
显然有暴力dp:\(f_x = \sum \max(f_y, 0)\),显然随着 \(x\) 增加,\(f_x\) 是不降的,所以 \(f\) 变成大于0 只会有一个时间点,考虑在对应的时间点连上向父亲的那条边,然后维护一下子树大小。\(f_x = sum + x\cdot siz > 0\),则 \(x > -\frac{sum}{siz}\),用树状数组维护 \(sum, siz\),用堆维护一下 \(-\frac{sum}{siz}\) 即可。
void solve() {
int n, m;
>> n >> m;
cin
std::vector<int> a(n), fa(n);
std::vector<std::vector<int>> g(n);
for (int i = 1; i < n; i++) {
>> fa[i];
cin [--fa[i]].push_back(i);
g}
std::vector<int> in(n), out(n);
int cnt = 0;
std::function<void(int)> dfs = [&](int x) {
[x] = cnt++;
infor (int y : g[x]) {
(y);
dfs}
[x] = cnt;
out};
(0);
dfs
std::priority_queue<PII, std::vector<PII>, std::greater<PII>> q;
std::vector<int> f(n), siz(n, 1);
std::vector<LL> sum(n);
auto find = [&](int i) {
while (i != f[i]) i = f[i] = f[f[i]];
return i;
};
for (int i = 0; i < n; i++) {
>> a[i];
cin [i] = i;
f[i] = a[i];
sum.push({-a[i], i});
q}
std::vector<std::array<int, 3>> qry(m);
for (int i = 0; i < m; i++) {
int u, x;
>> u >> x;
cin [i] = {x, u - 1, i};
qry}
std::sort(qry.begin(), qry.end());
<LL> fsum(n);
Fenwick<int> fsiz(n);
Fenwick
std::vector<LL> ans(m);
for (auto &[u, x, id] : qry) {
while (!q.empty() && q.top().first <= u) {
int x = q.top().second;
.pop();
q
if (!x || x != f[x]) continue;
int p = fa[x];
.add(in[p], sum[x]);
fsum.add(in[p], siz[x]);
fsiz[x] = p = find(p);
f[p] += sum[x], siz[p] += siz[x];
sumif (p) {
.add(in[fa[p]], -sum[x]);
fsum.add(in[fa[p]], -siz[x]);
fsiz= sum[p], v = siz[p];
LL u .push({u > 0 ? -(u / v) : (-u + v - 1) / v, p});
q}
}
[id] = (fsum.ask(in[x], out[x]) + a[x]) + 1LL * u * (fsiz.ask(in[x], out[x]) + 1);
ans}
for (auto x : ans) std::cout << x << "\n";
}
P5068 [Ynoi2015] 我回来了
亵渎的定义为:等概率随机在 \([L,R]\) 中选出一个整数作为伤害值 \(d\),对所有随从造成 \(d\) 点伤害,如果有随从死亡,则再次施放该法术,但伤害值不重新随机;如果没有随从死亡,则停止释放。 进行 \(m\) 次如下类型的操作: 1. 在场面上加入一个血量为 \(h\) 的随从,这里随从的血量都不能超过 \(n\); 2. 给定 \(L, R\),询问亵渎期望触发多少次;
\(1\le n\le 10^5, 1\le m\le 10^6\)
不难发现实际上是求 \(d = L \cdots R\) 的答案之和,然后 \(d\) 的答案是最小的 \(i\) 满足 \(((i-1) \cdot d, i \cdot d]\) 没有随从。
然后可以发现,答案之和是 \(n\log n\) 级别(调和级数),可以暴力维护,简单用线段树维护一下即可。
constexpr int N(1e5 + 5);
int n, m, ans[N];
[N];
LL cvoid add(int p, int x) {
for (; p <= n; p += p & -p) c[p] += x;
}
(int p) {
LL ask= 0;
LL r for (; p; p -= p & -p) r += c[p];
return r;
}
int k;
std::vector<int> s[N], q[N];
[1 << 18];
PII maxvoid pushup(int o) {
[o] = std::max(max[o << 1], max[o << 1 | 1]);
max}
(int l, int r) {
PII ask;
PII resfor (l += k, r += k; l < r; l >>= 1, r >>= 1) {
if (l & 1) smax(res, max[l++]);
if (r & 1) smax(res, max[--r]);
}
return res;
}
int main() {
// freopen("t.in", "r", stdin);
// freopen(".out", "w", stdout);
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n >> m;
= 1 << std::__lg(2 * n - 1);
k for (int i = 1; i <= n; i++) {
for (int j = 0; j < n; j += i) {
[j + k] = {i + j, j};
max[j].push_back(i + j);
s}
[i].resize((n - 1 + i) / i);
q}
for (int i = k - 1; i; i--) pushup(i);
while (m--) {
int opt, x, y;
std::cin >> opt >> x;
if (opt == 1) {
;
PII zwhile ((z = ask(0, x)).first >= x) {
= z.second;
y while (!s[y].empty() && s[y].back() >= x) {
int i = s[y].back() - y;
[i][y / i] = 1;
qint p = ans[i];
while (ans[i] < q[i].size() && q[i][ans[i]]) ans[i]++;
if (ans[i] - p) add(i, ans[i] - p);
[y].pop_back();
s}
[y + k] = {s[y].empty() ? 0 : s[y].back(), y};
max+= k;
y while (y > 1) pushup(y >>= 1);
}
} else {
std::cin >> y;
std::cout << ask(y) - ask(x - 1) + y - x + 1 << "\n";
}
}
return 0;
}
P4688 [Ynoi2016] 掉进兔子洞
一个长为 \(n\) 的序列 \(a\)。有 \(m\) 个询问,每次询问三个区间,把三个区间中同时出现的数一个一个删掉,问最后三个区间剩下的数的个数和,询问独立。
\(1\leq n , m \leq 10^5\),\(1 \leq a_i\leq 10^9\)。
就是求 \(|S_1\cap S_2 \cap S_3|\)。
莫队算一下,用bitset来求交即可。
因为空间不够开的,所以把询问分段做就行了。
constexpr int N(1e5 + 5), T(311);
struct Qry {
int l, r, id;
int block() const { return l / T; }
bool operator<(const Qry &rhs) const {
int u = block(), v = rhs.block();
return u == v ? u & 1 ? r < rhs.r : r > rhs.r : u < v;
}
};
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m;
std::cin >> n >> m;
std::vector<int> a(n), b;
for (auto &x : a) std::cin >> x;
= a;
b std::sort(b.begin(), b.end());
for (auto &x : a) x = std::lower_bound(b.begin(), b.end(), x) - b.begin();
std::vector<Qry> q;
std::vector<int> ans;
std::vector<std::bitset<N>> p;
auto solve = [&] {
std::sort(q.begin(), q.end());
int x = 0, y = -1;
std::vector<int> c(n);
std::bitset<N> now;
.reset();
now.resize(ans.size());
pfor (auto &v : p) v.set();
auto add = [&](int x) { now[x + c[x]++] = 1; };
auto del = [&](int x) { now[x + --c[x]] = 0; };
for (auto [l, r, id] : q) {
while (x > l) add(a[--x]);
while (y < r) add(a[++y]);
while (x < l) del(a[x++]);
while (y > r) del(a[y--]);
[id] &= now;
p}
for (int i = 0; i < ans.size(); i++) {
std::cout << ans[i] - 3 * p[i].count() << "\n";
}
};
for (int i = 0; i < m; i++) {
int now = ans.size();
.push_back(0);
ansfor (int j = 0, x, y; j < 3; j++) {
std::cin >> x >> y;
--, y--;
x.push_back({x, y, now});
q[now] += y - x + 1;
ans}
if (ans.size() == 33300 || i == m - 1) {
();
solve.clear();
q.clear();
ans}
}
return 0;
}
P5355 [Ynoi2017] 由乃的玉米田
给你一个序列 \(a\),长度为 \(n\),有 \(m\) 次操作,每次询问一个区间是否可以选出两个数它们的差为 \(x\),或者询问一个区间是否可以选出两个数它们的和为 \(x\),或者询问一个区间是否可以选出两个数它们的乘积为 \(x\) ,或者询问一个区间是否可以选出两个数它们的商为 \(x\)(没有余数) ,这四个操作分别为操作 \(1,2,3,4\)。
选出的这两个数可以是同一个位置的数。
所有输入的数在 \([0,10^5]\) 内,序列中的元素在 \([1,10^5]\) 内。
莫队,加减都用 bitset 即可。乘的话,对 \(x\) 分解质因数即可。
除的话,如果 \(x\) 大于根号,暴力枚举即可。
如果 \(x\) 小于根号,对于每个 \(x\) 分别做一下。对于每个数,维护一下它左边第一个符合条件的,然后查询区间最大值,搞一个 \(O(1)\) - \(O(\sqrt N)\) 的数据结构。
constexpr int N(1e5 + 5), T(384);
std::vector<int> fac[N];
void init(int n) {
for (int i = 1; i * i <= n; i++) {
for (int j = i * i; j <= n; j += i) {
[j].push_back(i);
fac}
}
}
std::bitset<N> now, rev;
int n, m, a[N];
struct Qry {
int opt, l, r, x, id;
bool operator<(const Qry &rhs) const {
return l / T == rhs.l / T ? l / T & 1 ? r < rhs.r : r > rhs.r : l < rhs.l;
}
} q[N];
int cnt[N];
void add(int x) {
if (1 == ++cnt[x]) now[x] = rev[N - x] = 1;
}
void del(int x) {
if (0 == --cnt[x]) now[x] = rev[N - x] = 0;
}
int ans[N];
std::vector<int> divq[T];
struct MaxDS {
int a[N], b[(N - 1 + T) / T];
void clear() {
(a, 0, sizeof a);
memset(b, 0, sizeof b);
memset}
void update(int x, int y) {
(a[x], y);
smax(b[x / T], y);
smax}
int ask(int l, int r) {
int res = 0, bl = l / T, br = r / T;
if (bl < br) {
for (int i = l; i < (bl + 1) * T; i++) smax(res, a[i]);
for (int i = bl + 1; i < br; i++) smax(res, b[i]);
for (int i = br * T; i <= r; i++) smax(res, a[i]);
} else {
for (int i = l; i <= r; i++) smax(res, a[i]);
}
return res;
}
} maxDS;
int pos[N];
int main() {
(1e5);
init// freopen("t.in", "r", stdin);
// freopen("t.out", "w", stdout);
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n >> m;
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
}
for (int i = 1; i <= m; i++) {
std::cin >> q[i].opt >> q[i].l >> q[i].r >> q[i].x;
[i].id = i;
q}
std::sort(q + 1, q + 1 + m);
for (int i = 1, x = 1, y = 0; i <= m; i++) {
if (q[i].opt == 4 && q[i].x < T) {
[q[i].x].push_back(i);
divqcontinue;
}
while (x > q[i].l) add(a[--x]);
while (y < q[i].r) add(a[++y]);
while (x < q[i].l) del(a[x++]);
while (y > q[i].r) del(a[y--]);
if (q[i].opt == 1) {
[q[i].id] = (now & now >> q[i].x).any();
ans} else if (q[i].opt == 2) {
[q[i].id] = (now & rev >> (N - q[i].x)).any();
ans} else if (q[i].opt == 3) {
for (int v : fac[q[i].x]) {
if (now[v] && now[q[i].x / v]) {
[q[i].id] = 1;
ansbreak;
}
}
} else {
for (int v = 1; v * q[i].x <= 100000; v++) {
if (now[v] && now[v * q[i].x]) {
[q[i].id] = 1;
ansbreak;
}
}
}
}
for (int i = 1; i < T; i++) {
if (divq[i].empty()) continue;
.clear();
maxDS(pos, 0, sizeof pos);
memsetfor (int j = 1; j <= n; j++) {
[a[j]] = j;
posif (a[j] % i == 0) maxDS.update(j, pos[a[j] / i]);
if (1LL * a[j] * i <= 1e5) maxDS.update(j, pos[a[j] * i]);
}
for (int j : divq[i]) {
[q[j].id] = maxDS.ask(q[j].l, q[j].r) >= q[j].l;
ans}
}
for (int i = 1; i <= m; i++) {
std::cout << (ans[i] ? "yuno\n" : "yumi\n");
}
return 0;
}
P5607 [Ynoi2013] 无力回天 NOI2017
给你一个长度为 n 的整数序列 \(a_1\), \(a_2\), \(\ldots\), \(a_n\) ,你需要实现以下两种操作,每个操作都可以用四个整数 \(opt\;l\;r\;v\) 来表示:
\(opt=1\) 时,代表把一个区间 \([l,r]\) 内的所有数都 xor 上 \(v\)。
\(opt=2\) 时, 查询一个区间 \([l,r]\) 内选任意个数(包括 \(0\) 个)数 xor 起来,这个值与 \(v\) 的最大 xor 和是多少。
\(1 \le n , m \le 5 \times 10^4\),值域在 \([0,10^9]\) 之间
差分一下变成单点修改区间线性基,线段树维护一下。
using LBasis = std::array<int, 31>;
void ins(LBasis &p, int x) {
if (!x) return;
for (int i = 30; i >= 0; i--) {
if (x >> i & 1 ^ 1) continue;
if (!p[i]) {
[i] = x;
pbreak;
}
^= p[i];
x }
}
(LBasis a, LBasis b) {
LBasis mergeauto r = a;
for (int i = 0; i <= 30; i++) ins(r, b[i]);
return r;
}
constexpr int N(5e4 + 5);
#define ls o << 1
#define rs o << 1 | 1
[N << 2];
LBasis pint n, a[N], sum[N << 2];
void pushup(int o) {
[o] = merge(p[ls], p[rs]);
p[o] = sum[ls] ^ sum[rs];
sum}
void build(int o, int l, int r) {
if (l == r) {
[o].fill(0);
p(p[o], sum[o] = a[l] ^ a[l - 1]);
insreturn;
}
int m = l + r >> 1;
(ls, l, m), build(rs, m + 1, r);
build(o);
pushup}
void ins(int o, int l, int r, int x, int y) {
if (l == r) {
[o].fill(0);
p(p[o], sum[o] ^= y);
insreturn;
}
int m = l + r >> 1;
<= m ? ins(ls, l, m, x, y) : ins(rs, m + 1, r, x, y);
x (o);
pushup}
(int o, int l, int r, int x, int y) {
LBasis askif (x <= l && r <= y) return p[o];
int m = l + r >> 1;
if (y <= m) return ask(ls, l, m, x, y);
if (x > m) return ask(rs, m + 1, r, x, y);
return merge(ask(ls, l, m, x, y), ask(rs, m + 1, r, x, y));
}
int qsum(int o, int l, int r, int x, int y) {
if (y < l || x > r) return 0;
if (x <= l && r <= y) return sum[o];
int m = l + r >> 1;
return qsum(ls, l, m, x, y) ^ qsum(rs, m + 1, r, x, y);
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m;
std::cin >> n >> m;
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
}
(1, 1, n);
buildwhile (m--) {
int opt, l, r, v;
std::cin >> opt >> l >> r >> v;
if (opt == 1) {
(1, 1, n, l, v);
insif (r < n) ins(1, 1, n, r + 1, v);
} else {
;
LBasis pif (l < r)
= ask(1, 1, n, l + 1, r);
p else
.fill(0);
pint t;
(p, t = qsum(1, 1, n, 1, l));
insfor (int i = 30; i >= 0; i--) smax(v, v ^ p[i]);
std::cout << v << "\n";
}
}
return 0;
}
P5356 [Ynoi2017] 由乃打扑克
给你一个长为 \(n\) 的序列 \(a\),需要支持 \(m\) 次操作,操作有两种:
- 查询区间 \([l,r]\) 的第 \(k\) 小值。
- 区间 \([l,r]\) 加上 \(k\)。
\(1\leq n,m\leq 10^5\),\(-2\times 10^4\leq\) 每次加上的数和原序列的数 \(\leq 2\times 10^4\)。
线段树,小于 \(B\) 的点才 pushup。
constexpr int N(1e5 + 5), T(512);
#define ls o << 1
#define rs o << 1 | 1
std::vector<int> g[N << 2];
int add[N << 2];
void pushup(int o) {
[o].resize(g[ls].size() + g[rs].size());
gfor (int i = 0, j = 0, k = 0; i < g[ls].size() || j < g[rs].size(); k++) {
if (j == g[rs].size() || i < g[ls].size() && g[ls][i] + add[ls] < g[rs][j] + add[rs]) {
[o][k] = g[ls][i++] + add[ls];
g} else {
[o][k] = g[rs][j++] + add[rs];
g}
}
}
void update(int o, int l, int r, int x, int y, int z) {
if (x <= l && r <= y) {
[o] += z;
addreturn;
}
int m = l + r >> 1;
if (x <= m) update(ls, l, m, x, y, z);
if (y > m) update(rs, m + 1, r, x, y, z);
if (r - l + 1 <= T) pushup(o);
}
std::vector<PII> id;
void get(int o, int l, int r, int x, int y, int z) {
+= add[o];
z if (x <= l && r <= y && r - l + 1 <= T) {
.push_back({o, z});
idreturn;
}
int m = l + r >> 1;
if (x <= m) get(ls, l, m, x, y, z);
if (y > m) get(rs, m + 1, r, x, y, z);
}
int a[N];
void build(int o, int l, int r) {
if (l == r) {
[o] = {a[l]};
greturn;
}
int m = l + r >> 1;
(ls, l, m), build(rs, m + 1, r);
buildif (r - l + 1 <= T) pushup(o);
}
int ask(int k) {
int l = INT_MAX, r = INT_MIN;
for (auto &[o, z] : id) {
(l, g[o].front() + z);
smin(r, g[o].back() + z);
smax}
while (l < r) {
int m = LL(l) + r + 1 >> 1, c = 0;
for (auto &[o, z] : id) {
+= std::lower_bound(g[o].begin(), g[o].end(), m - z) - g[o].begin();
c if (c >= k) break;
}
if (c >= k) {
= m - 1;
r } else {
= m;
l }
}
return l;
}
void solve() {
int n, m;
std::cin >> n >> m;
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
}
(1, 1, n);
build
while (m--) {
int opt, l, r, x;
std::cin >> opt >> l >> r >> x;
if (opt == 1) {
if (x < 1 || r - l + 1 < x) {
std::cout << "-1\n";
continue;
}
.clear();
id(1, 1, n, l, r, 0);
getstd::cout << ask(x) << "\n";
} else {
(1, 1, n, l, r, x);
update}
}
}
P5309 [Ynoi2011] 初始化
Mayuri 有 \(n\) 颗星星,每颗星星都有一个明亮度 \(A_{i}\) 。Mayuri 时常想知道一个区间 \([l,r]\) 内所有星星的明亮度的总和是多少。但是星星是会眨眼的,所以星星的明亮度是会变化的。有的时候,下标为 \(y,y+x,y+2x,y+3x,\ldots,y+kx\) 的星星的明亮度会增加 \(z\)。保证 \(y\leq x\)。
答案要对 \(10^{9}+7\) 取模。
\(1\leq n,m\leq 2\times 10^5\),\(1\leq y\leq x\leq n\),\(1\leq l\leq r\leq n\),\(0\leq A_i,z \leq 10^{9}+7\)。
简单根号分治一下即可。
constexpr int N(2e5 + 5), B(256);
struct SumDS {
[N], s1[N];
Z s0void add(int p, const Z &x) {
[p] += x;
s0[p / B] += x;
s1}
(int l, int r) {
Z ask= 0;
Z ans if (r - l > B * 2) {
while (l % B) ans += s0[l++];
while (l + B <= r) ans += s1[l / B], l += B;
}
while (l < r) ans += s0[l++];
return ans;
}
} sumDS;
int n, m;
[B][B + 1];
Z a
(int p) {
Z ask= 0;
Z ans for (int i = 1; i < B; i++) {
int k = p % i, v = p / i;
+= a[i][i] * v + a[i][k];
ans }
return ans;
}
(int l, int r) {
Z askreturn ask(r) - ask(l);
}
void solve() {
>> n >> m;
cin for (int i = 0; i < n; i++) {
int x;
>> x;
cin .add(i, x);
sumDS}
while (m--) {
int opt;
>> opt;
cin if (opt == 1) {
int x, y, _;
>> x >> y >> _;
cin = _;
Z z
--;
yif (x >= B) {
for (int i = y; i < n; i += x) {
.add(i, z);
sumDS}
} else {
for (int i = y + 1; i <= x; i++) {
[x][i] += z;
a}
}
} else {
int l, r;
>> l >> r;
cin --;
l= sumDS.ask(l, r) + ask(l, r);
Z ans << ans.x << "\n";
cout }
}
}
P5071 [Ynoi2015] 此时此刻的光辉
长为 \(n\) 的序列,有 \(m\) 次查询,每次查询一段区间的乘积的约数个数 \(\bmod 19260817\) 的值。
\(1\leq n,m\leq 10^5\),\(1 \leq a_i \leq10^9\)。
rho + 莫队,先要把小的质因子除掉。
constexpr int N(1e5 + 5), T(256);
struct Qry {
int l, r, id;
bool operator<(const Qry &rhs) const {
return l / T == rhs.l / T ? l / T & 1 ? r < rhs.r : r > rhs.r : l < rhs.l;
}
} q[N];
int ans[N];
std::vector<PII> fac[N];
[N * 30];
Z invvoid init() {
[1] = 1;
invfor (int i = 2; i <= 3000000; i++) {
[i] = (P - P / i) * inv[P % i];
inv}
}
int b[N * 30], t;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m;
std::cin >> n >> m;
();
sievefor (int i = 1, x; i <= n; i++) {
std::cin >> x;
for (auto j : primes) {
int c = 0;
while (x % j == 0) x /= j, c++;
if (c) fac[i].push_back({j, c});
}
auto f = factor(x);
std::sort(f.begin(), f.end());
for (int j = 0, k; j < f.size(); j = k) {
for (k = j + 1; k < f.size() && f[j] == f[k]; k++) ;
[i].push_back({f[j], k - j});
fac}
for (auto &[x, y] : fac[i]) {
[t++] = x;
b}
}
std::sort(b, b + t);
= std::unique(b, b + t) - b;
t for (int i = 1; i <= n; i++) {
for (auto &[x, y] : fac[i]) {
= std::lower_bound(b, b + t, x) - b;
x }
}
(b, 0, sizeof b);
memsetfor (int i = 1; i <= m; i++) {
std::cin >> q[i].l >> q[i].r;
[i].id = i;
q}
std::sort(q + 1, q + 1 + m);
= 1;
Z now ();
initauto add = [&](int i, int z) {
for (auto &[x, y] : fac[i]) {
*= inv[b[x] + 1];
now [x] += z * y;
b*= b[x] + 1;
now }
};
for (int i = 1, x = 1, y = 0; i <= m; i++) {
while (y < q[i].r) add(++y, 1);
while (x > q[i].l) add(--x, 1);
while (x < q[i].l) add(x++, -1);
while (y > q[i].r) add(y--, -1);
[q[i].id] = now.x;
ans}
for (int i = 1; i <= m; i++) {
std::cout << ans[i] << "\n";
}
return 0;
}
P5610 [Ynoi2013] 大学
一个长为 \(n\) 的非负整数序列 \(a\),支持以下两个操作:
1 l r x
:把区间 \([l,r]\) 中所有 \(x\) 的倍数除以 \(x\)。2 l r
:查询区间 \([l,r]\) 的和。本题强制在线,每次的 \(l,r,x\) 需要 xor 上上次答案,如果之前没有询问,则上次答案为 \(0\)。
\(1\leq n,m\leq 10^5\),\(0\leq a_i\leq 5\times 10^5\)
\(n\leq\) | \(10^1\) | \(10^2\) | \(10^3\) | \(10^4\) | \(10^5\) | \(10^6\) | \(10^7\) | \(10^8\) | \(10^9\) |
---|---|---|---|---|---|---|---|---|---|
\(\max\{\omega(n)\}\) | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 8 | 9 |
\(\max\{\operatorname{d}(n)\}\) | 4 | 12 | 32 | 64 | 128 | 240 | 448 | 768 | 1344 |
\(n\leq\) | \(10^{10}\) | \(10^{11}\) | \(10^{12}\) | \(10^{13}\) | \(10^{14}\) | \(10^{15}\) | \(10^{16}\) | \(10^{17}\) | \(10^{18}\) |
\(\max\{\omega(n)\}\) | 10 | 10 | 11 | 12 | 12 | 13 | 13 | 14 | 15 |
\(\max\{\operatorname{d}(n)\}\) | 2304 | 4032 | 6720 | 10752 | 17280 | 26880 | 41472 | 64512 | 103680 |
约数个数是200个级别,把每个数以及所有约数搞到一个有序数组里面,数组按照数字大小为第一关键字,数字出现位置为第二关键字排序。
每个数最多除 \(\log\) 次。如果一个数不整除 \(x\),就用并查集把他跳了。
用树状数组维护区间和。
constexpr int N(1e5 + 5), M(5e5 + 5);
int n, q, m, a[N], b[M], c[M], *fac, *pos, *f;
int find(int x) {
while (f[x] != x) x = f[x] = f[f[x]];
return x;
}
int get(int x, int p) {
int l = c[x], r = c[x + 1];
while (l < r) {
int m = l + r >> 1;
if (pos[m] < p) {
= m + 1;
l } else {
= m;
r }
}
return l;
}
void solve() {
>> n >> q;
cin
for (int i = 1; i <= n; i++) {
>> a[i];
cin [a[i]]++;
c(m, a[i]);
smax}
[0] = c[1] = 0;
c
for (int i = 2; i <= m; i++) {
for (int j = i; j <= m; j += i) {
[j]++;
b}
}
for (int i = 1; i <= m + 1; i++) b[i] += b[i - 1];
= new int[b[m]];
fac
for (int i = 2; i <= m; i++) {
for (int j = i; j <= m; j += i) {
[--b[j]] = i;
fac}
}
for (int i = 2; i <= m; i++) {
for (int j = i + i; j <= m; j += i) {
[i] += c[j];
c}
}
for (int i = 1; i <= m + 1; i++) c[i] += c[i - 1];
int tot = c[m];
= new int[tot];
pos = new int[tot];
f
for (int i = n; i; i--) {
if (a[i] <= 1) continue;
for (int j = b[a[i]]; j < b[a[i] + 1]; j++) {
int x = fac[j];
[--c[x]] = i;
pos}
}
std::iota(f, f + tot, 0);
<LL> fen(n);
Fenwickfor (int i = 1; i <= n; i++) {
.add(i - 1, a[i]);
fen}
= 0;
LL ans while (q--) {
int opt, l, r;
auto read = [&]() -> int {
;
LL x>> x;
cin ^= ans;
x return x;
};
>> opt;
cin = read(), r = read();
l
if (opt == 1) {
int x = read();
if (x <= 1) continue;
= get(x, l), r = get(x, r + 1);
l for (; l < r; l++) {
= find(l);
l if (l >= r) break;
int i = pos[l];
if (a[i] % x) {
[l] = l + 1;
fcontinue;
}
int v = a[i] / x;
.add(i - 1, v - a[i]);
fen[i] = v;
a}
} else {
= fen.ask(r) - fen.ask(l - 1);
ans << ans << "\n";
cout }
}
}
P5311 [Ynoi2011] 成都七中
给你一棵 \(n\) 个节点的树,每个节点有一种颜色,有 \(m\) 次查询操作。
查询操作给定参数 \(l\ r\ x\),需输出:
将树中编号在 \([l,r]\) 内的所有节点保留,\(x\) 所在连通块中颜色种类数。
每次查询操作独立。
对于 \(100\%\) 的数据,所有出现过的数在 \([1,10^5]\)
\(x\) 所在联通块,在点分树上的根是可以确定的:点分治的时候,\(x\) 到根的路径上的点全部都在 \([l, r]\) 内。
把询问挂到这个根上,每个根分别处理,如果一个点到根的路径上的点都在 \([l, r]\) 内,那么这个点合法。
每个点可以视为一个二元组 \((min, max)\) 表示到根路径上的最值,按照 \(max\) 排序,维护每个颜色的 \(min\) 的最大值,然后只需要查询 \(min \geq l\) 的数量即可。
constexpr int N(1e5 + 5);
int n, m, a[N], g[N * 2], deg[N];
std::array<int, 3> q[N];
int cqx[N], qx[N];
bool vq[N], vis[N];
int siz[N];
void getSize(int x, int p) {
[x] = 1;
sizfor (int i = deg[x]; i < deg[x + 1]; i++) {
if (g[i] != p && !vis[g[i]]) {
(g[i], x);
getSize[x] += siz[g[i]];
siz}
}
}
int getRoot(int x, int p, int s) {
for (int i = deg[x]; i < deg[x + 1]; i++) {
if (g[i] != p && !vis[g[i]] && siz[g[i]] * 2 > s) {
return getRoot(g[i], x, s);
}
}
return x;
}
int id[N], in[N], cur;
std::array<int, 3> seq[N * 20];
void dfs(int x, int p) {
auto &[l, r, c] = seq[cur - 1];
for (int i = cqx[x]; i < cqx[x + 1]; i++) {
int j = qx[i];
if (!vq[j] && q[j][0] <= l && r <= q[j][1]) {
[j] = true;
vq[j][2] = id[0];
q}
}
for (int i = deg[x]; i < deg[x + 1]; i++) {
int y = g[i];
if (y != p && !vis[y]) {
[cur++] = {std::min(l, y), std::max(r, y), a[y]};
seq(y, x);
dfs}
}
}
void solve(int x) {
(x, 0);
getSize= getRoot(x, 0, siz[x]);
x int o = ++id[0];
[o] = x;
id[cur++] = {x, x, a[x]};
seq(x, 0);
dfs[o + 1] = cur;
in
std::sort(seq + in[o], seq + in[o + 1], std::greater());
[x] = true;
visfor (int i = deg[x]; i < deg[x + 1]; i++) {
if (!vis[g[i]]) solve(g[i]);
}
}
int ans[N], min[N], c[N];
void add(int p, int x) {
for (; p <= n; p += p & -p) c[p] += x;
}
int ask(int p) {
int r = 0;
for (; p; p -= p & -p) r += c[p];
return r;
}
void solve() {
std::cin >> n >> m;
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
}
std::vector<PII> e(n - 1);
for (auto &[x, y] : e) {
std::cin >> x >> y;
[x]++, deg[y]++;
deg}
for (int i = 1; i <= n + 1; i++) deg[i] += deg[i - 1];
for (auto &[x, y] : e) {
[--deg[x]] = y, g[--deg[y]] = x;
g}
for (int i = 1; i <= m; i++) {
std::cin >> q[i][0] >> q[i][1] >> q[i][2];
[q[i][2]]++;
cqx}
for (int i = 1; i <= n + 1; i++) {
[i] += cqx[i - 1];
cqx}
for (int i = 1; i <= m; i++) {
[--cqx[q[i][2]]] = i;
qx}
(1);
solve
std::vector<int> p(m);
std::iota(p.begin(), p.end(), 1);
std::sort(p.begin(), p.end(), [&](int i, int j) {
return q[i][0] < q[j][0];
});
(cqx, 0, sizeof cqx);
memsetfor (int i = 1; i <= m; i++) {
[q[i][2]]++;
cqx}
for (int i = 1; i <= n + 1; i++) {
[i] += cqx[i - 1];
cqx}
for (int i : p) qx[--cqx[q[i][2]]] = i;
for (int i = 1; i <= 1e5; i++) min[i] = n + 1;
for (int i = 1; i <= n; i++) {
int k = in[i];
for (int j = cqx[i]; j < cqx[i + 1]; j++) {
int id = qx[j];
while (k < in[i + 1] && seq[k][0] >= q[id][0]) {
auto &[l, r, c] = seq[k++];
if (r < min[c]) {
(min[c], -1);
add(min[c] = r, 1);
add}
}
[id] = ask(q[id][1]);
ans}
while (k > in[i]) {
auto &[l, r, c] = seq[--k];
if (min[c] != n + 1) {
(min[c], -1);
add[c] = n + 1;
min}
}
}
for (int i = 1; i <= m; i++) {
std::cout << ans[i] << "\n";
}
}
P3934 [Ynoi2016] 炸脖龙 I
给一个长为\(n\)的序列,\(m\)次操作,每次操作:
1、区间\([l,r]\)加\(x\)
2、对于区间\([l,r]\),查询:
\(a[l]^{a[l+1]^{a[l+2]^{\dots ^{a[r]}}}} \mod p\)
对于100%的数据,\(n , m \le 500000\) , 序列中每个数在\([1,2\cdot 10^9]\)内,\(p \le 2 \cdot 10^7\), 每次加上的数在\([0,2\cdot 10^9]\)内
拓展欧拉定理板子题,对 \(p\) 取 \(phi\) 每两次就至少减半。
constexpr int N(5e5 + 5), M(2e7 + 5);
int np[M], pr[M / 10], pn, phi[M];
void sieve() {
[1] = 1;
phiconst int n = 2e7;
for (int i = 2; i <= n; i++) {
if (!np[i]) pr[++pn] = i, np[i] = pn, phi[i] = i - 1;
for (int j = 1; j <= pn && pr[j] * i <= n; j++) {
[i * pr[j]] = j;
npif (np[i] == j) {
[i * pr[j]] = phi[i] * pr[j];
phibreak;
}
[i * pr[j]] = phi[i] * (pr[j] - 1);
phi}
}
}
int n, m;
[N], d[N];
LL cvoid add(int p, LL x) {
[p] += x;
dfor (; p <= n; p += p & -p) c[p] += x;
}
(int p) {
LL ask= 0;
LL r for (; p; p -= p & -p) r += c[p];
return r;
}
int cal(int l, int r, int p, LL x) {
if (l == r || p == 1) return x >= p ? x % p + p : x;
int k = cal(l + 1, r, phi[p], x + d[l + 1]), ans = 1;
(p);
Barrett bauto mul = [&](int x, int y) -> int {
auto z = 1LL * x * y;
return z >= p ? b.rem(z) + p : z;
};
if (x >= p) x = b.rem(x) + p;
while (k) {
if (k & 1) ans = mul(ans, x);
if (k >>= 1) x = mul(x, x);
}
return ans;
}
void solve() {
();
sieve>> n >> m;
cin
for (int i = 1, d = 0; i <= n; i++) {
int x;
>> x;
cin (i, x - d);
add= x;
d }
while (m--) {
int opt, l, r, x;
>> opt >> l >> r >> x;
cin if (opt == 1) {
(l, x), add(r + 1, -x);
add} else {
<< cal(l, r, x, ask(l)) % x << '\n';
cout }
}
}
P6749 『MdOI R3』Yoshino
Yoshino 给了你一个长度为 \(n\) 的序列,第 \(i\) 项为 \(a_i\)。
现在 Yoshino 会对数列进行 \(m\) 次操作。
操作分成两种:
\(1\ l\ r\ x\) Yoshino 把数列下标在 \([l,r]\) 区间内的数修改为了一个从 \(x\) 开始公差为 \(1\) 的等差数列。
\(2\) Yoshino 需要查询整个数列中的逆序对个数。逆序对的定义为数对 \((i,j)\) 满足 \(i<j\) 且 \(a_i>a_j\)。
\(1\le n,m,a_i\le 3\times 10^4\),\(1\le l\le r\le n\),\(1\le x\le 3\times 10^4-r+l\)。
颜色段均摊之后,把一段的信息记在一个端点上,然后变成查询左右比它大/小的,用cdq分治解决。
做这个题的时候,我开始把他搞成对角线修改查询,这个玩意很毒瘤,详见ptz22-s-qingyu-round,基本上不是人写的东西。但是你可以发现可以当作所有的 \(x, x+1, \dots, x+r-l\) 全部堆积在 \(l\) 上面,你在颜色分裂的时候再把一部分从前一个 \(l\) 搬到另一个位置,这样的话,就从对角线修改变成竖直线修改了,这个就是傻逼了。
constexpr int N(3e4 + 5), M(3e4 + 1), O(N * 20);
int n, m, a[N];
[N];
LL ans
[N][3];
LL cvoid add(int p, int x) {
[3] = {(p - 1LL) * p * x, (1LL - 2 * p) * x, x};
LL vfor (; p < M; p += p & -p) {
[p][0] += v[0], c[p][1] += v[1], c[p][2] += v[2];
c}
}
(int p) {
LL ask[3] = {};
LL vfor (int i = p; i; i -= i & -i) {
[0] += c[i][0], v[1] += c[i][1], v[2] += c[i][2];
v}
return (v[2] * p + v[1]) * p + v[0];
}
struct Opt {
int x, l, r, k, id;
} t1[O], t2[O], t[O];
void cal(Opt *a, int l, int r) {
if (l == r) return;
int m = l + r >> 1;
(a, l, m), cal(a, m + 1, r);
calfor (int i = l, j = m + 1, k = l; k <= r; k++) {
if (j > r || i <= m && a[i].x > a[j].x) {
[k] = a[i++];
t(t[k].l, t[k].k), add(t[k].r + 1, -t[k].k);
add} else {
[k] = a[j++];
tif (t[k].id != -1) ans[t[k].id] += t[k].k * (ask(t[k].r) - ask(t[k].l - 1));
}
}
for (int i = l; i <= m; i++) {
(a[i].l, -a[i].k), add(a[i].r + 1, a[i].k);
add}
for (int i = l; i <= r; i++) a[i] = t[i];
}
void solve() {
std::cin >> n >> m;
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
}
std::set<int> s;
for (int i = 1; i <= n + 1; i++) s.insert(i);
int qc = 0, cnt = 0;
auto add = [&](int l, int r, int k) {
int x = a[l], y = x + r - l;
[++cnt] = {l, x, y, k, qc};
t1[cnt] = {-l, M - y, M - x, k, qc};
t2};
auto split = [&](int x) {
auto it = --s.upper_bound(x);
if (x == *it) return it;
[x] = a[*it] + x - *it;
aint l = *it, r = *++it - 1, u = a[x], v = u + r - x;
[++cnt] = {l, u, v, -1, -1};
t1[cnt] = {-l, M - v, M - u, -1, -1};
t2[++cnt] = {x, u, v, 1, -1};
t1[cnt] = {-x, M - v, M - u, 1, -1};
t2return s.insert(x).first;
};
for (int i = 1; i <= n; i++) {
(i, i, 1);
add}
while (m--) {
int opt, l, r, x;
std::cin >> opt;
if (opt == 1) {
std::cin >> l >> r >> x;
auto lt = split(l), rt = split(r + 1);
for (auto it = lt; it != rt; it = s.erase(it)) {
(*it, *std::next(it) - 1, -1);
add}
.insert(l), a[l] = x;
s(l, r, 1);
add} else {
++;
qc}
}
// std::cerr << "???\n";
(t1, 1, cnt);
cal(t2, 1, cnt);
cal
for (int i = 0; i < qc; i++) {
[i + 1] += ans[i];
ansstd::cout << ans[i] / 2 << "\n";
}
}
P7811 [JRKSJ R2] 你的名字。
给你一个长为 \(n\) 的序列 \(a\),有 \(m\) 次查询,每次查询区间 \([l,r]\) 模 \(k\) 意义下的最小值。
\(1\le n,m\le3\times10^5\),\(1\le a_i,k\le 10^5\)。
先序列分块,把边角暴力掉,询问区间小的也暴力掉,然后变成在整块的区间上面查询。
根号分治,如果 \(k < B\),对每个模 \(k\) 后的数组预处理一下,随便查。
如果 \(k \geq B\),转换成区间 \(\geq ik\) 的最小值,扫描线之后,搞一个 \(O(\sqrt n)\) 单点修改,\(O(1)\) 查询区间最小值的结构。
这个结构怎么搞,搞一个 st 表,长度为 \(n\) 的st表单点修改的代价是 \(1 + 2 + 4 + 8 + \dots + n = O(n)\)。
constexpr int N(3e5 + 5), V(1e5), B(444), S(N / B + 5);
int n, m, tot, a[N], b[N], c[N], min[S], st[S][10], lg[S], bel[N];
void build() {
for (int i = tot - 1; i >= 0; i--) {
for (int j = 1; i + (1 << j) <= tot; j++) {
[i][j] = std::min(st[i][j - 1], st[i + (1 << j - 1)][j - 1]);
st}
}
}
void add(int p, int x) {
= bel[p];
p for (int i = p; i >= 0; i--) {
for (int j = lg[p - i] + 1; i + (1 << j) <= tot; j++) st[i][j] = x;
}
}
int ask(int u, int v) {
int w = lg[v - u];
return std::min(st[u][w], st[v - (1 << w)][w]);
}
struct Qry {
int l, r, k, id;
bool operator<(const Qry &rhs) const {
return k < rhs.k;
}
} q[N];
std::vector<int> fac[V + 5];
void solve() {
>> n >> m;
cin = (n - 1) / B + 1;
tot [0] = -1;
lgfor (int i = 1; i <= tot; i++) lg[i] = lg[i >> 1] + 1;
for (int i = 0; i <= n; i++) bel[i] = i / B;
for (int i = 0; i < n; i++) {
>> a[i];
cin }
std::vector<int> out(m, 1e9);
{
std::vector<Qry> t;
.reserve(m);
tfor (int i = 0; i < m; i++) {
int l, r, k;
>> l >> r >> k;
cin --;
l
(k);
Barrett barif (r - l <= B * 2) {
for (int j = l; j < r; j++) {
(out[i], bar.rem(a[j]));
smin}
} else {
for (int j = l, z = bel[l] * B + B; j < z; j++) smin(out[i], bar.rem(a[j]));
for (int j = bel[r] * B; j < r; j++) smin(out[i], bar.rem(a[j]));
.push_back({bel[l] + 1, bel[r], k, i});
t[k]++;
c}
}
for (int i = 1; i <= V; i++) {
[i + 1] += c[i];
c}
for (auto &v : t) q[--c[v.k]] = v;
= t.size();
m }
std::vector<int> ans(m, 1e9);
for (int i = 0; i < c[B]; i++) {
int k = q[i].k;
if (!i || k != q[i - 1].k) {
for (int j = 0; j < tot; j++) st[j][0] = 1e9;
(k);
Barrett barfor (int j = 0; j < n; j++) {
(st[bel[j]][0], bar.rem(a[j]));
smin}
();
build}
(ans[i], ask(q[i].l, q[i].r));
smin}
for (int i = B; i <= V; i++) {
[0].push_back(i);
facfor (int j = i; j <= V; j += i) {
[j].push_back(i);
fac}
}
std::vector<int> p(n);
std::iota(p.begin(), p.end(), 0);
std::sort(p.begin(), p.end(), [&](int i, int j) { return a[i] > a[j]; });
(st, 0x3f, sizeof st);
memset
for (int i = V, j = 0; i >= 0; i--) {
while (j < n && a[p[j]] >= i) {
(p[j], a[p[j]]), j++;
add}
for (int x : fac[i]) {
for (int k = c[x]; k < c[x + 1]; k++)
(ans[k], ask(q[k].l, q[k].r) - i);
smin}
}
for (int i = 0; i < m; i++) smin(out[q[i].id], ans[i]);
for (int x : out) cout << x << '\n';
}
P6779 [Ynoi2009] rla1rmdq
给定一棵 \(n\) 个节点的树,树有边权,与一个长为 \(n\) 的序列 \(a\)。
定义节点 \(x\) 的父亲为 \(fa(x)\),根 \(rt\) 满足 \(fa(rt)=rt\)。
定义节点 \(x\) 的深度 \(dep(x)\) 为其到根简单路径上所有边权和。
有 \(m\) 次操作:
1 l r
:对于 \(l \le i \le r\), \(a_i := fa(a_i)\) 。
2 l r
:查询对于 \(l \le i \le r\),最小的 \(dep(a_i)\)。对于 \(100\%\) 的数据,\(1\le n,m\le 2\cdot 10^5\),\(1\le a_i\le n\),边权在 \([0,10^9]\) 之间。
分块之后,离线对每个块单独处理。
维护可能成为最小值的点的集合 \(S\),想象一下根和这些点的虚树,每次跳的话就是把叶子往上拨,记一个 \(vis\) 表示一个点是否被访问过,每次块整体跳的时候,直接暴力把集合中的点往上跳,如果发现已经访问过了,就删掉这个点,这样的话,只会跳 \(O(n)\) 次。
问题是有单独跳的,记录整块跳的次数 \(cnt\),还有某个点作为 \(S\) 中元素跟着往上跳的次数 \(c_x\),那么你单独跳的时候只要把缺的次数补上就行了,这样需要查询 k 级祖先,由于有 \(O(n\sqrt n)\) 次这样查询,看似要长链剖分搞一个 \(O(1)\) 查询的操作。
但是实际上只要HLD就行了,因为每个点到根只有 \(O(\log n)\) 条重链,重链总数是 \(O(n\log n)\) 的,所以复杂度没啥影响。
constexpr int N(2e5 + 5);
int n, m, r, dep[N], fa[N], in[N], id[N], dfn, siz[N], son[N], top[N];
std::vector<PII> g[N];
[N];
LL d
void dfs1(int x) {
[x] = 1;
sizfor (auto [y, z] : g[x]) {
if (y == fa[x]) continue;
[y] = x;
fa[y] = d[x] + z;
d[y] = dep[x] + 1;
dep(y);
dfs1[x] += siz[y];
sizif (siz[y] > siz[son[x]]) son[x] = y;
}
}
void dfs2(int x) {
[x] = ++dfn, id[in[x]] = x;
inif (!son[x]) return;
[son[x]] = top[x], dfs2(son[x]);
topfor (auto [y, z] : g[x]) {
if (y == son[x] || y == fa[x]) continue;
[y] = y, dfs2(y);
top}
}
int jmp(int x, int k) {
if (dep[x] <= k) return r;
for (;;) {
int d = in[x] - in[top[x]];
if (k <= d) return id[in[x] - k];
-= d + 1;
k = fa[top[x]];
x }
}
int a[N], b[N], vis[N];
bool inp[N];
void solve() {
std::cin >> n >> m >> r;
for (int i = 1, x, y, z; i < n; i++) {
std::cin >> x >> y >> z;
[x].push_back({y, z});
g[y].push_back({x, z});
g}
[r] = top[r] = r, dfs1(r), dfs2(r);
fa
for (int i = 1; i <= n; i++) std::cin >> a[i];
std::vector<std::array<int, 3>> q(m);
for (auto &[x, y, z] : q) std::cin >> x >> y >> z;
std::vector<LL> res(m, 1e18);
for (int l = 1, r, B = sqrt(n), t; l <= n; l = r + 1) {
= std::min(n, l + B);
r std::vector<int> p(r - l + 1);
= 1e18;
LL ans = 0;
t for (int i = l; i <= r; i++) {
if (vis[a[i]] != l) {
[a[i]] = l;
vis[t++] = i;
p[i] = true;
inp
(ans, d[a[i]]);
smin}
}
.resize(t);
p
int cnt = 0;
for (int z = 0; z < m; z++) {
auto [opt, x, y] = q[z];
(x, l), smin(y, r);
smaxif (x > y) continue;
if (x == l && y == r) {
if (opt == 1) {
++;
cnt= 0;
t for (int x : p) {
[x] = fa[a[x]], b[x]++;
aif (vis[a[x]] != l) {
[a[x]] = l;
vis[t++] = x;
p(ans, d[a[x]]);
smin} else {
[x] = false;
inp}
}
.resize(t);
p} else {
(res[z], ans);
smin}
} else {
if (opt == 1) {
for (int i = x; i <= y; i++) {
[i] = jmp(a[i], 1 + cnt - b[i]);
a[i] = cnt;
bif (vis[a[i]] != l) {
[a[i]] = l;
vis(ans, d[a[i]]);
sminif (!inp[i]) {
[i] = true;
inp.push_back(i);
p}
}
}
} else {
for (int i = x; i <= y; i++) {
[i] = jmp(a[i], cnt - b[i]);
a[i] = cnt;
b(res[z], d[a[i]]);
smin}
}
}
}
}
for (int i = 0; i < m; i++) {
if (q[i][0] == 2) {
std::cout << res[i] << "\n";
}
}
}
P6782 [Ynoi2008] rplexq
给定一棵 \(n\) 个节点的有根树,第 \(i\) 个点的编号是 \(i\)。
有 \(m\) 次询问,每次询问给出 \(l,r,x\),求有多少点编号的二元组 \((i,j)\) 满足 \(l \le i < j \le r\) 且 \(i\) 和 \(j\) 的最近公共祖先是节点 \(x\)。
\(1\le n,m\le 2\cdot 10^5\),\(1 \le l,r,x \le n\)。
这个答案主要可以表示成 \(cnt_x^2 - \sum_{y} cnt_y^2\),考虑后者的计算。
对于每个 \(x\) 分别处理,每个儿子的子树染一个颜色,然后这个可以用莫队解决。
问题是 \(\sum siz_x\) 可能太大了,考虑把前 \(T\) 大的子树用暴力计算(后面说)。
类似HLD,前 \(T\) 大的儿子全部都是重儿子,那每跳一次轻边,子树大小至少扩大 \(T+1\) 倍。
令 \(T = n^{1/k}\),\(\sum siz\) 就变成 \(O(kn)\) 了,这样莫队复杂度就对了。
然后对于暴力,就是要做 \(O(nT)\) 次询问子树内 \([l, r]\) 的点数,用dfs序搞一下变成二维数点,可以用扫描线加 \(O(\sqrt n)\) - \(O(1)\) 数据结构算一下。
但是这题空间比较小,不能把 \(O(nT)\) 个询问全部存下来。
考虑把dfs序那维作为扫描,这样的话,每个子树的dfs序是不相交的,每个询问只需要记录一个值。
constexpr int N(2e5 + 5), T(20);
int n, m, r, siz[N], in[N], id[N], dfn;
std::vector<int> g[N];
void dfs(int x) {
[x] = 1;
siz[x] = ++dfn, id[in[x]] = x;
infor (int y : g[x]) {
[y].erase(std::find(g[y].begin(), g[y].end(), x));
g(y);
dfs[x] += siz[y];
siz}
std::sort(g[x].begin(), g[x].end(), [&](int i, int j) {
return siz[i] > siz[j];
});
}
struct Fenw {
int c[N];
void add(int p, int x) {
for (; p <= n; p += p & -p) c[p] += x;
}
int ask(int p) {
int r = 0;
for (; p; p -= p & -p) r += c[p];
return r;
}
int ask(int l, int r) {
return ask(r) - ask(l - 1);
}
} fen;
struct Qry {
int l, r, x, id;
} q[N], tmp[N];
int cx[N], v0[N], v1[N];
[N];
LL ansvoid dfs2(int x) {
for (int j = cx[x]; j < cx[x + 1]; j++) {
[j] = fen.ask(q[j].l, q[j].r);
v0}
.add(x, 1);
fenfor (int i = 0; i < g[x].size(); i++) {
int y = g[x][i];
if (i < T) {
for (int j = cx[x]; j < cx[x + 1]; j++) {
[j] = fen.ask(q[j].l, q[j].r);
v1}
(y);
dfs2for (int j = cx[x]; j < cx[x + 1]; j++) {
int v = fen.ask(q[j].l, q[j].r) - v1[j];
[q[j].id] -= 1LL * v * v;
ans}
} else {
(y);
dfs2}
}
for (int j = cx[x]; j < cx[x + 1]; j++) {
int v = fen.ask(q[j].l, q[j].r) - v0[j];
[q[j].id] += 1LL * v * v;
ans}
}
int bel[N];
void work(int x) {
int deg = g[x].size();
if (deg <= T || cx[x] == cx[x + 1]) return;
// std::cerr << "wk " << x << "\n";
int sz = 0;
for (int i = T; i < deg; i++) {
int y = g[x][i];
for (int j = in[y]; j < in[y] + siz[y]; j++) {
[j] = i - T;
v0[++sz] = id[j];
v1}
}
std::sort(v1 + 1, v1 + 1 + sz);
[0] = 0, v1[sz + 1] = n + 1;
v1
int B = sz / sqrt(cx[x + 1] - cx[x]);
(B, 1);
smaxfor (int i = 1, p = 0; i <= sz + 1; i++) {
int x = (i - 1) / B;
while (p < v1[i]) bel[++p] = x;
}
std::sort(q + cx[x], q + cx[x + 1], [&](const auto &i, const auto &j) {
return bel[i.l] == bel[j.l] ? bel[i.l] & 1 ? i.r < j.r : i.r > j.r : i.l < j.l;
});
= 0;
LL now
std::vector<int> s(deg - T), t(sz);
for (int i = 0; i < sz; i++) t[i] = v0[in[v1[i + 1]]];
auto add = [&](int x) {
+= 2 * ++s[t[x - 1]] - 1;
now };
auto del = [&](int x) {
-= 2 * --s[t[x - 1]] + 1;
now };
int l = 1, r = 0;
for (int i = cx[x]; i < cx[x + 1]; i++) {
while (v1[l - 1] >= q[i].l) add(--l);
while (v1[r + 1] <= q[i].r) add(++r);
while (v1[l] < q[i].l) del(l++);
while (v1[r] > q[i].r) del(r--);
[q[i].id] -= now;
ans}
}
void solve() {
>> n >> m >> r;
cin for (int i = 1, x, y; i < n; i++) {
>> x >> y;
cin [x].push_back(y);
g[y].push_back(x);
g}
(r);
dfs
for (int i = 0; i < m; i++) {
int l, r, x;
>> l >> r >> x;
cin [x]++;
cx[i] = {l, r, x, i};
tmpif (l <= x && x <= r) ans[i]--;
}
for (int i = 1; i <= n; i++) cx[i + 1] += cx[i];
for (int i = 0; i < m; i++) q[--cx[tmp[i].x]] = tmp[i];
(r);
dfs2
for (int i = 1; i <= n; i++) {
(i);
work}
for (int i = 0; i < m; i++) {
<< ans[i] / 2 << "\n";
cout }
}
P7983 [JRKSJ R3] practiceZ
琴琴给了你两个长为 \(n\) 的序列 \(a,b\),请你支持三种操作共 \(m\) 次:
1 l r x
,将 \(a\) 序列的区间 \([l,r]\) 中的所有数修改为 \(x\)。2 l r y
,将 \(b\) 序列的区间 \([l,r]\) 中的所有数修改为 \(y\)。3 l r
,求 \(\sum_{i=l}^r \sum_{j=1}^{b_i} a_j\)。答案对 \(2^{32}\) 取模。对于 \(100\%\) 的数据,\(1\le n\le 5\times 10^5\),\(1\le m\le 3\times 10^5\),\(1\le a_i,x\le 10^9\),\(1\le b_i,y\le n\)。
没什么营养的题。
首先观察询问,发现询问是对 b 序列的区间询问,因此可以对 b 分块。
对 a 的操作都是区间颜色段均摊,因而转换成 \(O(m)\) 次区间加减。
分块后,对于两边的散块,暴力。于是我们需要对于 a 维护一种支持 \(O(\sqrt N)\) 区间加,\(O(1)\) 查询前缀和的数据结构。
此处有一个关键的卡常点:往距离短的那个块边界暴力,这样可以减少一半的常数。
处理完散块后,所有的询问都是对整块的询问了,我们离线之后逐块处理。
在一个块中,维护 b 的颜色段,不是整块的颜色段每个操作只会产生 \(O(1)\) 个,所以所有块的散段个数是 \(O(m)\) 个,需要支持对这些散段进行 a 的前缀和查询,以及每块处理时进行的 \(O(m)\) 次对 a 的修改,维护一个 \(O(1)\) - \(O(\sqrt N)\) 的数据结构即可。
而对于是整块的颜色段,需要在逐块处理前查询好其对应的 a 的前缀和,并在覆盖整段时打上标记。
修改 a 时,需要查询这个东西对当前块的影响,如果当前整块是相同颜色,那么可以直接计算,否则需要计算的东西和之前类似,需要维护一个 \(O(\sqrt N)\) 区间修改,\(O(1)\) 查询的数据结构。
上面说的这一堆东西都是根号平衡的。
逐块处理的常数很大,所以块大小要开大点。
我最先的提交有些细节没实现好,复杂度是错的,居然也能过,可见上述的关键卡常点效果显著。
namespace FS {
using namespace std;
/* 64分木。
insert, erase
[]での存在判定
next, prev
*/
struct FastSet {
using uint = unsigned;
using ull = unsigned long long;
int bsr(ull x) { return 63 - __builtin_clzll(x); }
int bsf(ull x) { return __builtin_ctzll(x); }
static constexpr uint B = 64;
int n, lg;
<vector<ull>> seg;
vector(int _n) : n(_n) {
FastSetdo {
.emplace_back((_n + B - 1) / B, -1ULL);
seg= (_n + B - 1) / B;
_n } while (_n > 1);
= int(seg.size());
lg }
bool operator[](int i) const { return (seg[0][i / B] >> (i % B) & 1) != 0; }
void insert(int i) {
for (int h = 0; h < lg; h++) {
[h][i / B] |= 1ULL << (i % B);
seg/= B;
i }
}
void erase(int i) {
for (int h = 0; h < lg; h++) {
[h][i / B] &= ~(1ULL << (i % B));
segif (seg[h][i / B]) break;
/= B;
i }
}
// x以上最小の要素を返す。存在しなければ n。
int next(int i) {
for (int h = 0; h < lg; h++) {
if (i / B == seg[h].size()) break;
= seg[h][i / B] >> (i % B);
ull d if (!d) {
= i / B + 1;
i continue;
}
// find
+= bsf(d);
i for (int g = h - 1; g >= 0; g--) {
*= B;
i += bsf(seg[g][i / B]);
i }
return i;
}
return n;
}
// x以下最大の要素を返す。存在しなければ -1。
int prev(int i) {
if (i < 0) return -1;
if (i >= n) i = n - 1;
for (int h = 0; h < lg; h++) {
if (i == -1) break;
= seg[h][i / B] << (63 - i % 64);
ull d if (!d) {
= i / B - 1;
i continue;
}
// find
+= bsr(d) - (B - 1);
i for (int g = h - 1; g >= 0; g--) {
*= B;
i += bsr(seg[g][i / B]);
i }
return i;
}
return -1;
}
// [l, r) 内の要素を全部集める
<int> collect(int l, int r) {
vector<int> res;
vectorint x = l - 1;
while (1) {
= next(x + 1);
x if (x >= r) break;
.emplace_back(x);
res}
return res;
}
};
}
constexpr int N(5e5 + 5), M(3e5 + 5), N1((N >> 5) + 1), N2((N >> 10) + 1), N3((N >> 15) + 1), B(3500);
int n, m, a[N], b[N], ta[N], tb[N];
template <class T>
struct SumDS1 {
[N + 33], s1[N1 + 33], s2[N2 + 33], s3[N3 + 5];
T s0void clear() {
(s0, 0, sizeof s0);
memset(s1, 0, sizeof s1);
memset(s2, 0, sizeof s2);
memset(s3, 0, sizeof s3);
memset}
void add(int p, T x) {
[p++] += x;
s0#define F(s0) \
switch (p & 3) { \
case 0: break; \
case 1: s0[p] += x, s0[p + 1] += x, s0[p + 2] += x; p += 3; break; \
case 2: s0[p] += x, s0[p + 1] += x; p += 2; break; \
case 3: s0[p++] += x; break; \
}
#define G(s0) while (p & 31) s0[p] += x, s0[p + 1] += x, s0[p + 2] += x, s0[p + 3] += x, p += 4;
(s0)G(s0)p >>= 5;
F(s1)G(s1)p >>= 5;
F(s2)G(s2)p >>= 5;
F(s3)while (p < N3) s3[p] += x, s3[p + 1] += x, s3[p + 2] += x, s3[p + 3] += x, p += 4;
F#undef F
#undef G
}
(int p) {
T askreturn s0[p] + s1[p >> 5] + s2[p >> 10] + s3[p >> 15];
}
};
template <class T>
struct SumDS2 {
[N], s1[N1], s2[N2], s3[N3];
T s0void clear() {
(s0, 0, sizeof s0);
memset(s1, 0, sizeof s1);
memset(s2, 0, sizeof s2);
memset(s3, 0, sizeof s3);
memset}
void add(int p, T x) {
[p] += x, s1[p >> 5] += x, s2[p >> 10] += x, s3[p >> 15] += x;
s0}
(int p) {
T ask{};
T rint k = p & ~31;
while (p >= k) r += s0[p--];
if (p < 0) return r;
>>= 5, k = p & ~31;
p while (p >= k) r += s1[p--];
if (p < 0) return r;
>>= 5, k = p & ~31;
p while (p >= k) r += s2[p--];
if (p < 0) return r;
>>= 5;
p while (p >= 0) r += s3[p--];
return r;
}
};
using U2 = __attribute((vector_size(4 * 2))) U;
struct Opt {
int opt, l, r, x;
;
U y} op[N * 4];
int on;
[M];
U ans<U2> ds1;
SumDS1<U2> ds2;
SumDS2
(int i) { // O(T)
U askAauto s = ds2.ask(i);
return s[0] * (1 + i) - s[1];
}
void addA(int i, U x) { // O(1)
.add(i, U2{x, x * i});
ds2}
(int i) { // O(1)
U askA1auto s = ds1.ask(i);
return s[0] * (1 + i) - s[1];
}
void addA1(int i, U x) { // O(T)
.add(i, U2{x, x * i});
ds1}
void addB(int i, U x) { // O(T)
.add(n - i + 1, U2{x, x * i});
ds1}
void work(int l, int r) {
int tagB = 0;
= r - l + 1, now = 0;
U len
auto askB = [&](int i) -> U { // O(1)
if (tagB) return i <= tagB ? (tagB - i + 1) * len : 0;
auto s = ds1.ask(n - i + 1);
return s[0] * (1 - i) + s[1];
};
::FastSet s(r - l + 1);
FS
auto split = [&](int x) {
if (!s[x]) b[x + l] = b[s.prev(x) + l], s.insert(x);
};
.clear();
ds1for (int i = l; i <= r; i++) {
(b[i], 1);
addB}
for (int i = 1; i <= n; i++) {
+= (a[i] - a[i - 1]) * askB(i);
now }
.clear();
ds2for (int i = 1; i <= n; i++) {
(i, a[i] - a[i - 1]);
addA}
for (int i = 0; i < on; i++) {
if (op[i].opt == 1) {
+= op[i].x * askB(op[i].l);
now (op[i].l, op[i].x);
addA} else {
if (op[i].l > r || op[i].r < l) continue;
int x = std::max(l, op[i].l), y = std::min(r, op[i].r);
if (op[i].opt == 2) {
if (tagB && x == l && y == r) {
= 0;
tagB = 0;
now } else {
if (tagB) addB(tagB, len), tagB = 0;
(x - l);
splitif (y != r) split(y + 1 - l);
for (int i = x - l, j, u, w; i < y + 1 - l; i = j) {
= i + l, j = s.next(i + 1), w = i - j;
u (b[u], w);
addB+= w * askA(b[u]);
now if (u > x) s.erase(i);
}
}
[x] = op[i].x;
bif (x == l && y == r) {
= b[x];
tagB } else {
(b[x], y - x + 1);
addB}
+= (y - x + 1) * op[i].y;
now } else {
[op[i].x] += now;
ans}
}
}
}
void solve() {
>> n >> m;
cin
for (int i = 1; i <= n; i++) {
>> a[i];
cin (i, a[i] - a[i - 1]);
addA1}
for (int i = 1; i <= n; i++) {
>> b[i];
cin }
(ta, a, sizeof a);
memcpy(tb, b, sizeof b);
memcpy
::FastSet sa(n + 10), sb(n + 10);
FS
auto splitA = [&](int x) {
if (!sa[x]) a[x] = a[sa.prev(x)], sa.insert(x);
};
auto splitB = [&](int x) {
if (!sb[x]) b[x] = b[sb.prev(x)], sb.insert(x);
};
int qc = 0;
while (m--) {
int opt, l, r, x;
>> opt >> l >> r;
cin if (opt == 1) {
>> x;
cin (l), splitA(++r);
splitA
int d = 0;
for (int i = l; i < r; i = sa.next(i + 1)) {
int u = i, w = x - a[u], o = d + w;
if (o) {
(u, o);
addA1[on++] = {1, u, 0, o};
op}
= -w;
d if (i > l) sa.erase(i);
}
if (d) {
(r, d);
addA1[on++] = {1, r, 0, d};
op}
[l] = x;
a} else if (opt == 2) {
>> x;
cin (l), splitB(r + 1);
splitBfor (int i = sb.next(l + 1); i != r + 1; i = sb.next(i)) {
[i] = 0;
b.erase(i);
sb}
[l] = x;
b[on++] = {opt, l, r, x, askA1(x)};
op} else {
= qc++;
x
auto cal = [&](int l, int r) {
if (l > r) return 0u;
int o = b[sb.prev(l)];
= 0;
U a for (int i = l; i <= r; i++) {
+= askA1(b[i] ? o = b[i] : o);
a }
return a;
};
if (r - l + 1 <= B * 2) {
[x] += cal(l, r);
ans} else {
int kl = (l - 1) / B * B + 1, kr = kl + B - 1;
if (l - kl << 1 < B) {
[x] -= cal(kl, l - 1);
ans= kl;
l } else {
[x] += cal(l, kr);
ans= kr + 1;
l }
= (r - 1) / B * B + 1, kr = std::min(n, kl + B - 1);
kl if (kr - r << 1 < B) {
[x] -= cal(r + 1, kr);
ans= kr;
r } else {
[x] += cal(kl, r);
ans= kl - 1;
r }
[on++] = {opt, l, r, x};
op}
}
}
(a, ta, sizeof a);
memcpy(b, tb, sizeof b);
memcpy
// std::cerr << (double)clock() / CLOCKS_PER_SEC << "\n";
for (int i = 1, j; i <= n; i = j + 1) {
= std::min(i + B - 1, n);
j (i, j);
work// std::cerr << (double)clock() / CLOCKS_PER_SEC << "\n";
}
for (int i = 0; i < qc; i++) {
<< ans[i] << '\n';
cout }
std::cerr << (double)clock() / CLOCKS_PER_SEC << "\n";
}
End
最后修改于 2022-10-25