树分治
点分治
1、通过重儿子这个特性,每次遍历重儿子,每次找到的重儿子都能将重儿子的每个子节点的子树都几乎平均分割为 \(log\) 大小,所以最多 \(log\) 层,每层差不多 \(n\) 个节点,最终时间复杂度就为 \(nlogn\)。
2、可以用于解决两点之间的距离为 \(k\) 的数目有多少等等。
3、时间复杂度:\(O(nlogn)\),空间复杂度:\(O(n)\)。
4、模版:洛谷P3806。
template<typename T>
struct tree_div{
struct node{
int to, nxt;
T w;
node(int to = 0, int nxt = 0, T w = 0) : to(to), nxt(nxt), w(w) {}
};
int n, m, cnt, q, sum, root, k, tot;
queue<T> tag;
vector<node> edg;
vector<T> head, vis, res, Q, mar;
vector<T> siz, siz_mx, dist, sto;
tree_div(int n_, int m_, int q_, int k_ = 10000000)
: head(n_ + 1), siz(n_ + 1, 1), siz_mx(n_ + 1),
dist(n_ + 1), vis(k_ + 1), res(q_ + 1), Q(q_ + 1),
mar(n_ + 1), sto(n_ + 1), edg(m_ << 1 | 1) {
cnt = 0;
tot = 0;
sum = n_;
n = n_;
m = m_;
q = q_;
k = k_;
}
inline void add(int u, int v, const T &w) {
++cnt;
edg[cnt].to = v;
edg[cnt].nxt = head[u];
edg[cnt].w = w;
head[u] = cnt;
}
inline void init(int n_, int m_, int q_, int k_ = 10000000) {
cnt = 0;
tot = 0;
sum = n_;
n = n_;
m = m_;
q = q_;
k = k_;
head.assign(n_ + 1, 0);
siz.assign(n_ + 1, 1);
siz_mx.assign(n_ + 1, 0);
dist.assign(n_ + 1, 0);
vis.assign(k_ + 1, 0);
res.assign(q_ + 1, 0);
Q.assign(q_ + 1, 0);
mar.assign(n_ + 1, 0);
sto.assign(n_ + 1, 0);
edg.assign(m_ << 1 | 1, node());
}
inline void calcsiz(int u, int pa) {// O(n)的时间复杂度得到重心
siz[u] = 1;
siz_mx[u] = 0;
for (int i = head[u]; i; i = edg[i].nxt) {
int v = edg[i].to;
T w = edg[i].w;
if (v == pa || mar[v]) {
continue;
}
calcsiz(v, u);
siz[u] += siz[v];
siz_mx[u] = max(siz_mx[u], siz[v]);
}
siz_mx[u] = max(siz_mx[u], sum - siz[u]);
if (siz_mx[u] < siz_mx[root]) {
root = u;
}
}
inline void calcdist(int u, int pa) {// 计算重心的每个子节点中到重心的距离
sto[++tot] = dist[u];
for (int i = head[u]; i; i = edg[i].nxt) {
int v = edg[i].to;
T w = edg[i].w;
if (v == pa || mar[v]) {
continue;
}
dist[v] = dist[u] + w;
calcdist(v, u);
}
}
inline void dfz(int u, int pa) {
vis[0] = 1;// 根距离为0
tag.push(0);
mar[u] = 1;// 标记重心,表示经过这个点的路径都跑完了
for (int i = head[u]; i; i = edg[i].nxt) {
int v = edg[i].to;
T w = edg[i].w;
if (v == pa || mar[v]) {
continue;
}
dist[v] = w;
calcdist(v, u);
for (int j = 1; j <= tot; j++) {
for (int z = 1; z <= q; z++) {// 处理查询的所有点
if (Q[z] >= sto[j]) {
res[z] |= vis[Q[z] - sto[j]];
}
}
}
for (int j = 1; j <= tot; j++) {
if (sto[j] <= k) {
tag.push(sto[j]);
vis[sto[j]] = 1;// 标记距离值
}
}
tot = 0;
}
while (!tag.empty()) {// 去除之前找的所有值
vis[tag.front()] = 0;
tag.pop();
}
for (int i = head[u]; i; i = edg[i].nxt) {
int v = edg[i].to;
T w = edg[i].w;
if (v == pa || mar[v]) {
continue;
}
sum = siz[v];
root = 0;
siz_mx[root] = mof;
calcsiz(v, u);// 重新找新的重心节点
calcsiz(root, 0);
dfz(root, u);
}
}
inline void deal() {
root = 0;
siz_mx[root] = mof;
calcsiz(1, 0);
calcsiz(root, 0);
dfz(root, 0);
}
};
void solve() {
int n, m;
std::cin >> n >> m;
tree_div<int> tr(n, n - 1, m);
for (int i = 1, u, v, w; i < n; i++) {
std::cin >> u >> v >> w;
tr.add(u, v, w);
tr.add(v, u, w);
}
for (int i = 1; i <= m; i++) {
std::cin >> tr.Q[i];
}
tr.deal();
for (int i = 1; i <= m; i++) {
std::cout << (tr.res[i] ? "AYE" : "NAY") << '\n';
}
}
拓展
1、题目:给定一棵 \(n\) 个节点的树,每条边有边权,求出树上两点距离小于等于 \(k\) 的点对数量。
2、一样的,只不过这次需要用树状数组维护下权值数目。
3、例题:洛谷P4178。
template<typename T>
struct Fenwick{
int n;
vector<T> a;
Fenwick() {}
Fenwick(int n_) : a(n_ + 1) {
n = n_;
}
inline int low_bit(int x) {
return x & -x;
}
inline void add(int x, const T &v) {
for (int i = x; i <= n; i += low_bit(i)) {
a[i] += v;
}
}
inline T query(int x) {
T sum{};
for (int i = x; i >= 1; i -= low_bit(i)) {
sum += a[i];
}
return sum;
}
inline T rangeQuery(int L, int R) {
return query(R) - query(L - 1);
}
inline int select(const T &k) {
int x = 0;
T cnt{};
for (int i = 1 << __lg(n); i; i >>= 1) {
if (x + i <= n && cnt + a[i + x] <= k) {
x += i;
cnt += a[x];
}
}
return x;
}
};
template<typename T>
struct tree_div{
struct node{
int to, nxt;
T w;
node(int to = 0, int nxt = 0, T w = 0) : to(to), nxt(nxt), w(w) {}
};
int n, m, cnt, sum, root, k, tot, res;
queue<T> tag;
vector<int> head, mar;
vector<T> siz, siz_mx, dist, sto;
vector<node> edg;
Fenwick<T> calc;
tree_div() {}
tree_div(int n_, int m_)
: head(n_ + 1), siz(n_ + 1, 1), siz_mx(n_ + 1), dist(n_ + 1),
mar(n_ + 1), sto(n_ + 1), edg(m_ << 1 | 1) {
cnt = 0;
tot = 0;
res = 0;
sum = n_;
n = n_;
m = m_;
}
inline void add(int u, int v, const T &w) {
++cnt;
edg[cnt].to = v;
edg[cnt].nxt = head[u];
edg[cnt].w = w;
head[u] = cnt;
}
inline void init(int n_, int m_) {
cnt = 0;
tot = 0;
res = 0;
sum = n_;
n = n_;
m = m_;
head.assign(n_ + 1, 0);
siz.assign(n_ + 1, 1);
siz_mx.assign(n_ + 1, 0);
dist.assign(n_ + 1, 0);
mar.assign(n_ + 1, 0);
sto.assign(n_ + 1);
edg.assign(m_ << 1 | 1, node());
}
inline void calcsiz(int u, int pa) {
siz[u] = 1;
siz_mx[u] = 0;
for (int i = head[u]; i; i = edg[i].nxt) {
int v = edg[i].to;
T w = edg[i].w;
if (v == pa || mar[v]) {
continue;
}
calcsiz(v, u);
siz[u] += siz[v];
siz_mx[u] = max(siz_mx[u], siz[v]);
}
siz_mx[u] = max(siz_mx[u], sum - siz[u]);
if (siz_mx[u] < siz_mx[root]) {
root = u;
}
}
inline void calcdist(int u, int pa) {
sto[++tot] = dist[u];
for (int i = head[u]; i; i = edg[i].nxt) {
int v = edg[i].to;
T w = edg[i].w;
if (v == pa || mar[v]) {
continue;
}
dist[v] = dist[u] + w;
calcdist(v, u);
}
}
inline void dfz(int u, int pa) {
calc.add(1, 1);
tag.push(0);
mar[u] = 1;
for (int i = head[u]; i; i = edg[i].nxt) {
int v = edg[i].to;
T w = edg[i].w;
if (v == pa || mar[v]) {
continue;
}
dist[v] = w;
calcdist(v, u);
for (int j = 1; j <= tot; j++) {
if (sto[j] <= k) {
T val = k - sto[j];
res += calc.rangeQuery(1, val + 1);
}
}
for (int j = 1; j <= tot; j++) {
if (sto[j] <= k) {
tag.push(sto[j]);
calc.add(sto[j] + 1, 1);
}
}
tot = 0;
}
while (!tag.empty()) {
calc.add(tag.front() + 1, -1);
tag.pop();
}
for (int i = head[u]; i; i = edg[i].nxt) {
int v = edg[i].to;
T w = edg[i].w;
if (v == pa || mar[v]) {
continue;
}
sum = siz[v];
root = 0;
siz_mx[root] = mof;
calcsiz(v, u);
calcsiz(root, 0);
dfz(root, u);
}
}
inline void deal() {
std::cin >> k;
calc = Fenwick<T>(k + 1);
root = 0;
siz_mx[root] = mof;
calcsiz(1, 0);
calcsiz(root, 0);
dfz(root, 0);
}
};
void solve() {
int n;
std::cin >> n;
tree_div<int> tr(n, n - 1);
for (int i = 1, u, v, w; i < n; i++) {
std::cin >> u >> v >> w;
tr.add(u, v, w);
tr.add(v, u, w);
}
tr.deal();
std::cout << tr.res << '\n';
}

浙公网安备 33010602011771号