十月的风吹过金色的国庆
希望你收获满满的笑容与温暖,
假期的每一刻,都值得被珍惜。
祝老友,假期愉快🍂
进入正题吧!
浅谈重链剖分
介绍
重链剖分就是一些善(du)良(liu)出题人为了恶心我们加强难度,把序列上的问题转到树上的解决方案。具体方法就是把树分成一个个链,通过进行对链的修改实现对整棵树的修改。
步骤
DFS1
第一次深度优先搜索维护出每个节点的深度父亲,子树大小,还有你的重链要往哪一个儿子走(简称重儿子)。
因为我们要让划分出的重链数量尽可能的少,所以说我们要挑子树最大的那个儿子作为重儿子。
这样的话树上的每一条路径都能拆成不超过 logn 条重链。
因为如果向下经过轻边的话,所在子树大小至少会除以二。
DFS2
要想在把树划分成重链,显然不止这些。
因为要对重链进行修改,所以说一条重链的节点编号必须是连续的。所以说要维护每个节点所在重链的顶端是谁(或者说是它属于哪一条重链),以及这个节点的新编号。
最后根据编号将它维护到树形数据结构上。
子树修改/查询
最简单的一集。
注意到子树内的编号都是连续的,所以直接对 [x,x+sizex−1] 进行操作。
做完了。
链上修改/查询
类似一个求 LCA 的过程。
当两个点不在同一条重链上的时候,就跳那个重链顶端深度更小的结点,对所在的重链进行修改。
跳到同一条重链直接做就行了。
时间复杂度显然是 O(qlog2n) 的
例题
P3384
模板题,没啥好说的,按照上面的直接做就行了
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define lowbit(x) (x&-x)
const int N = 1e5 + 10;
int n, T, r, P, cnt, son[N], depth[N], fa[N], sz[N], id[N], top[N], w[N], c1[N], c2[N];
vector<int> g[N];
//---------这里是数据结构部分-------------
inline void add(int l, int r, int x) {
int ad1 = (l - 1) * x % P, ad2 = r *x % P;
for (int i = l; i <= n; i += lowbit(i)) c1[i] = (c1[i] + x) % P, c2[i] = (c2[i] + ad1) % P;
for (int i = r + 1; i <= n; i += lowbit(i)) c1[i] = (c1[i] - x + P) % P, c2[i] = (c2[i] - ad2 + P) % P;
}
inline int query(int x) {
int ret = 0;
for (int i = x; i; i -= lowbit(i)) ret = (ret + c1[i] * x % P) % P, ret = (ret - c2[i] + P) % P;
return ret;
}
inline int ask(int l, int r) {
return (query(r) - query(l - 1) + P) % P;
}
//---------这里是树剖部分----------
//dfs基本上都是千篇一律,直接背就行
inline void dfs1(int f, int u) {
fa[u] = f, sz[u] = 1, depth[u] = depth[f] + 1;
int tmp = -1;
for (int v : g[u]) {
if (v == f) continue;
dfs1(u, v);
sz[u] += sz[v];
if (sz[v] > tmp) tmp = sz[v], son[u] = v;
}
}
inline void dfs2(int f, int u) {
top[u] = f, id[u] = ++cnt;
add(id[u], id[u], w[u]);
if (son[u] == 0) return;
dfs2(f, son[u]);
for (int v : g[u]) {
if (v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
}
//这里是路径,不同的题可能略有修改
inline void addPath(int u, int v, int k) {
k %= P;
while (top[u] != top[v]) {
if (depth[top[u]] < depth[top[v]]) swap(u, v); //LCA 显然是优先跳小的
add(id[top[u]], id[u], k);
u = fa[top[u]];
}
if (depth[u] > depth[v]) swap(u, v); //深度大的在后
add(id[u], id[v], k);
}
//和addPath一样就不讲了
inline int askPath(int u, int v) {
int res = 0;
while (top[u] != top[v]) {
if (depth[top[u]] < depth[top[v]]) swap(u, v);
res = (res + ask(id[top[u]], id[u])) % P, u = fa[top[u]];
}
if (depth[u] > depth[v]) swap(u, v);
res = (res + ask(id[u], id[v])) % P;
return res;
}
//子树没啥好讲的
inline int askSon(int u) {
return ask(id[u], id[u] + sz[u] - 1);
}
inline void addSon(int u, int k) {
k %= P;
add(id[u], id[u] + sz[u] - 1, k);
}
//---------这里是主函数部分----------
signed main() {
cin >> n >> T >> r >> P;
for (int i = 1; i <= n; i++) cin >> w[i];
for (int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs1(0, r), dfs2(r, r);
int op, u, v, k;
while (T--) {
cin >> op >> u;
if (op == 1) {
cin >> v >> k;
addPath(u, v, k);
} else if (op == 2) {
cin >> v;
cout << askPath(u, v) << endl;
} else if (op == 3) {
cin >> k;
addSon(u, k);
} else {
cout << askSon(u) << endl;
}
}
return 0;
}
P1505
#include <bits/stdc++.h>
using namespace std;
#define debug cerr<<"The code runs successfully.\n";
#define endl '\n'
#define TRACE 1
#define tcout TRACE && cout
#define fst ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define int long long
#define lson(x) (x<<1)
#define rson(x) (x<<1|1)
const int P = 998244353;
const int Base = 33331;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 10, M = 2e6 + 10;
struct segtree {
int l, r, sum, max, min, lazy;
} s[N << 2];
pair<int, int> edge[N];
vector<pair<int, int>> g[N];
int n, q, id[N], cnt, son[N], siz[N], fa[N], w[N], top[N], dep[N], wssb[N];
inline void calc(int p) {
s[p].lazy ^= 1;
s[p].sum = -s[p].sum;
s[p].max = -s[p].max;
s[p].min = -s[p].min;
swap(s[p].max, s[p].min);
}
inline void pushup(int p) {
s[p].sum = s[lson(p)].sum + s[rson(p)].sum;
s[p].min = min(s[lson(p)].min, s[rson(p)].min);
s[p].max = max(s[lson(p)].max, s[rson(p)].max);
}
inline void pushdown(int p) {
if (!s[p].lazy) return;
calc(lson(p)), calc(rson(p));
s[p].lazy ^= 1;
}
inline void build(int p, int l, int r) {
s[p].l = l, s[p].r = r;
if (l == r) {
s[p].sum = s[p].min = s[p].max = w[l];
return;
}
int mid = l + r >> 1;
build(lson(p), l, mid);
build(rson(p), mid + 1, r);
pushup(p);
}
inline void change1(int p, int x, int k) {
if (s[p].l == s[p].r) {
s[p].sum = s[p].max = s[p].min = k;
return;
}
pushdown(p);
int mid = s[p].l + s[p].r >> 1;
if (mid >= x) change1(lson(p), x, k);
else change1(rson(p), x, k);
pushup(p);
}
inline void change2(int p, int l, int r) {
if (s[p].r < l || r < s[p].l) return;
if (s[p].l >= l && s[p].r <= r) {
calc(p);
return;
}
pushdown(p);
int mid = s[p].l + s[p].r >> 1;
change2(lson(p), l, r), change2(rson(p), l, r);
pushup(p);
}
inline int asksum(int p, int l, int r) {
if (s[p].r < l || r < s[p].l) return 0;
if (s[p].l >= l && s[p].r <= r) return s[p].sum;
pushdown(p);
return asksum(lson(p), l, r) + asksum(rson(p), l, r);
}
inline int askmax(int p, int l, int r) {
if (s[p].r < l || r < s[p].l) return -INF;
if (s[p].l >= l && s[p].r <= r) return s[p].max;
pushdown(p);
return max(askmax(lson(p), l, r), askmax(rson(p), l, r));
}
inline int askmin(int p, int l, int r) {
if (s[p].r < l || r < s[p].l) return INF;
if (s[p].l >= l && s[p].r <= r) return s[p].min;
pushdown(p);
return min(askmin(lson(p), l, r), askmin(rson(p), l, r));
}
inline void dfs1(int f, int u) {
fa[u] = f, dep[u] = dep[f] + 1, siz[u] = 1;
int tmp = -1;
for (auto e : g[u]) {
int v = e.first;
if (v == f) continue;
wssb[v] = e.second;
dfs1(u, v);
siz[u] += siz[v];
if (tmp < siz[v]) {
son[u] = v, tmp = siz[v];
}
}
}
inline void dfs2(int f, int u) {
id[u] = ++cnt, w[cnt] = wssb[u], top[u] = f;
if (son[u]) dfs2(f, son[u]);
for (auto e : g[u]) {
int v = e.first;
if (fa[u] == v || v == son[u]) continue;
dfs2(v, v);
}
}
inline void update1(int x, int k) {
int ccf;
if (dep[edge[x].first] > dep[edge[x].second]) ccf = edge[x].first;
else ccf = edge[x].second;
change1(1, id[ccf], k);
}
inline void update2(int x, int y) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
change2(1, id[top[x]], id[x]);
x = fa[top[x]];
}
if (dep[x] > dep[y]) swap(x, y);
if (x != y) change2(1, id[x] + 1, id[y]); //就是这里需要注意一下
}
inline int querysum(int x, int y) {
int res = 0;
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
res += asksum(1, id[top[x]], id[x]);
x = fa[top[x]];
}
if (dep[x] > dep[y]) swap(x, y);
if (x != y) res += asksum(1, id[x] + 1, id[y]); //就是这里需要注意一下
return res;
}
inline int querymax(int x, int y) {
int res = -INF;
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
res = max(res, askmax(1, id[top[x]], id[x]));
x = fa[top[x]];
}
if (dep[x] > dep[y]) swap(x, y);
if (x != y) res = max(res, askmax(1, id[x] + 1, id[y]));
return res;
}
inline int querymin(int x, int y) {
int res = INF;
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
res = min(res, askmin(1, id[top[x]], id[x]));
x = fa[top[x]];
}
if (dep[x] > dep[y]) swap(x, y);
if (x != y) res = min(res, askmin(1, id[x] + 1, id[y]));
return res;
}
signed main() {
fst;
cin >> n;
for (int i = 1; i < n; i++) {
int u, v, w;
cin >> u >> v >> w;
u++, v++;
g[u].push_back({v, w});
g[v].push_back({u, w});
edge[i] = {u, v};
}
dfs1(0, 1);
dfs2(1, 1);
build(1, 1, n);
cin >> q;
while (q--) {
string op;
int x, y;
cin >> op >> x >> y;
if (op == "C") {
update1(x, y);
} else if (op == "N") {
x++, y++;
update2(x, y);
} else if (op == "SUM") {
x++, y++;
cout << querysum(x, y) << endl;
} else if (op == "MAX") {
x++, y++;
cout << querymax(x, y) << endl;
} else {
x++, y++;
cout << querymin(x, y) << endl;
}
}
return 0;
}
P1505 [国家集训队] 旅游 - 洛谷
也是几乎板子,线段树稍微改一改。
但是这里有一个 trick 就是边权转点权。
对于每一条边,把它的边权转移到通向的深度更大的点。
这里有一个细节,进行链上修改的时候,跳到了同一条重链,这个时候深度更小的点的点权是不会进行修改的,然后就可以直接做了。
2652

被折叠的 条评论
为什么被折叠?



