文章目录
例题引入(来自OI-wiki)
代码模板
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define all(x) begin(x),end(x)
#define debug(x) cout<<#x<<": "<<x<<endl;
using namespace std;
using ll = long long;
const int N = 1e5 + 10;
// 计算每个不同子树中有多少种不同的颜色 (本题也可以在dfs序上莫队)
int n;
vector<int> g[N];
int sz[N], son[N], col[N], L[N], R[N], node[N], totdfn;
int ans[N], cnt[N], totColor;
void add(int u) {
if (cnt[col[u]] == 0) ++ totColor;
cnt[col[u]] ++;
}
void del(int u) {
cnt[col[u]] --;
if (cnt[col[u]] == 0) -- totColor;
}
int getAns() {
return totColor;
}
void dfs(int u, int fa) {
L[u] = ++ totdfn;
node[totdfn] = u;
sz[u] = 1;
for (int &v: g[u]) {
if(v == fa) continue;
dfs(v, u);
sz[u] += sz[v];
if (!son[u] or sz[son[u]] < sz[v]) son[u] = v;
}
R[u] = totdfn;
}
void DFS(int u, int fa, bool keep) {
// 先算轻儿子,后算重儿子,且重儿子保留
for (int v: g[u]) {
if (v == fa or v == son[v]) continue;
DFS(v, u, false);
}
if (son[u]) DFS(son[u], u, true);
// 因轻儿子未保留,需要重新加回来
for (int v: g[u]) {
if (v == fa or v == son[u]) continue;
for (int i = L[v]; i <= R[v]; i ++) add(node[i]);
}
add(u); // 加上节点u自己
ans[u] = getAns(); // 得到以u为根的子树答案
if (keep == false) {
for (int i = L[u]; i <= R[u]; i ++) del(node[i]);
}
}
int main() {
cin.tie(0);
ios::sync_with_stdio(false);
cin >> n;
for (int i = 1; i <= n; i ++) cin >> col[i];
for (int i = 1; i < n; i ++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1, 0);
dfs(1, 0, false);
for (int i = 1; i <= n; i ++) cout << ans[i] << " \n"[i == n];
return 0;
}
CF600E Lomsat gelral
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define all(x) begin(x),end(x)
#define debug(x) cout<<#x<<": "<<x<<endl;
using namespace std;
using ll = long long;
const int N = 1e5 + 10;
int n, col[N], cnt[N];
vector<int> g[N];
int sz[N], son[N], L[N], R[N], node[N], totdfn;
void dfs(int u, int fa) {
L[u] = ++ totdfn; node[totdfn] = u;
sz[u] = 1;
for (int v: g[u]) {
if (v == fa) continue;
dfs(v, u);
sz[u] += sz[v];
if (!son[u] or sz[v] > sz[son[u]]) son[u] = v;
}
R[u] = totdfn;
}
ll ans[N], res, mxcnt;
void add(int u) {
cnt[col[u]] ++;
if (cnt[col[u]] > mxcnt) {
res = col[u];
mxcnt = cnt[col[u]];
} else if (cnt[col[u]] == mxcnt) {
res += col[u];
}
}
void del(int u) {
// 这里没有对res和mxcnt维护,因为已经清零了
cnt[col[u]] --;
}
void DFS(int u, int fa, bool keep) {
for (int v: g[u]) {
if (v == fa or v == son[u]) continue;
DFS(v, u, false);
}
if (son[u]) DFS(son[u], u, true);
for (int v: g[u]) {
if (v == fa or v == son[u]) continue;
for (int i = L[v]; i <= R[v]; i ++) add(node[i]);
}
add(u);
ans[u] = res;
if (keep == false) {
res = mxcnt = 0; // 全部清零,del函数中无需对res和mxcnt维护
for (int i = L[u]; i <= R[u]; i ++) del(node[i]);
}
}
int main() {
cin.tie(0);
ios::sync_with_stdio(false);
cin >> n;
for (int i = 1; i <= n; i ++) cin >> col[i];
for (int i = 1; i < n; i ++) {
int x, y;
cin >> x >> y;
g[x].push_back(y);
g[y].push_back(x);
}
dfs(1, 0);
DFS(1, 0, false);
for (int i = 1; i <= n; i ++) cout << ans[i] << " \n"[i == n];
return 0;
}
CF570D Tree Requests
1
≤
n
,
m
≤
5
e
5
1\le n,m \le5e5
1≤n,m≤5e5
sol:
维护同一深度上
26
26
26个字母的情况。
只有当这
26
26
26个字母中出现次数为奇数的个数不大于
1
1
1时才能构成回文。
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define all(x) begin(x),end(x)
#define debug(x) cout<<#x<<": "<<x<<endl;
using namespace std;
using ll = long long;
const int N = 5e5 + 10;
int n, m, d[N];
char s[N];
vector<int> g[N];
int sz[N], son[N], L[N], R[N], node[N], totdfn;
void dfs(int u, int fa) {
sz[u] = 1, d[u] = d[fa] + 1;
L[u] = ++ totdfn; node[totdfn] = u;
for (auto& v: g[u]) {
if (v == fa) continue;
dfs(v, u);
sz[u] += sz[v];
if (!son[u] or sz[v] > sz[son[u]]) son[u] = v;
}
R[u] = totdfn;
}
vector<pair<int, int>> Q[N];
array<int, 26> cnt[N];
bool ok[N];
bool check(int dep) {
int odd = 0;
for (auto& x: cnt[dep]) {
if (x & 1) odd ++;
}
return odd <= 1;
}
void add(int u) {
cnt[d[u]][s[u] - 'a'] ++;
}
void del(int u) {
cnt[d[u]][s[u] - 'a'] --;
}
void DFS(int u, int fa, bool keep) {
for (auto& v: g[u]) {
if (v == fa or v == son[u]) continue;
DFS(v, u, false);
}
if (son[u]) DFS(son[u], u, true);
for (auto& v: g[u]) {
if (v == fa or v == son[u]) continue;
for (int i = L[v]; i <= R[v]; i ++) add(node[i]);
}
add(u);
for (auto &[dep, id]: Q[u]) ok[id] = check(dep);
if (keep == false) {
for (int i = L[u]; i <= R[u]; i ++) del(node[i]);
}
}
int main() {
cin.tie(0);
ios::sync_with_stdio(false);
cin >> n >> m;
for (int i = 2; i <= n; i ++) {
int p; cin >> p;
g[p].push_back(i);
}
cin >> s + 1;
for (int i = 1; i <= m; i ++) {
int a, b;
cin >> a >> b;
Q[a].emplace_back(b, i);
}
dfs(1, 0);
DFS(1, 0, false);
for (int i = 1; i <= m; i ++) cout << (ok[i] ? "Yes" : "No") << '\n';
return 0;
}
wannafly Day2 E 阔力梯的树
sol:
用一个
s
e
t
set
set维护当前所有节点。
插入时在set中查找前驱后继,并计算贡献。
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define all(x) begin(x),end(x)
#define debug(x) cout<<#x<<": "<<x<<endl;
using namespace std;
using ll = long long;
const int N = 1e5 + 10;
int n;
vector<int> g[N];
int sz[N], son[N], L[N], R[N], node[N], totdfn;
set<int> S;
ll ans[N], res;
void add(int u) {
if (S.empty()) {
S.insert(u);
return ;
}
auto it = S.lower_bound(u);
if (it == S.begin()) {
ll x = *S.begin();
res += (x - u) * (x - u);
} else if (it == S.end()) {
ll x = *--S.end();
res += (x - u) * (x - u);
} else {
ll y = *it, x = *--it;
res += 2ll*u*u - 2*u*(x+y) + 2*x*y;
}
S.insert(u);
}
void dfs(int u, int fa) {
sz[u] = 1;
L[u] = ++ totdfn; node[totdfn] = u;
for (auto& v: g[u]) {
if (v == fa) continue;
dfs(v, u);
sz[u] += sz[v];
if (!son[u] or sz[v] > sz[son[u]]) son[u] = v;
}
R[u] = totdfn;
}
void DFS(int u, int fa, bool keep) {
for (auto& v: g[u]) {
if (v == fa or v == son[u]) continue;
DFS(v, u, false);
}
if (son[u]) DFS(son[u], u, true);
for (auto& v: g[u]) {
if (v == fa or v == son[u]) continue;
for (int i = L[v]; i <= R[v]; i ++) add(node[i]);
}
add(u);
ans[u] = res;
if (keep == false) {
S.clear();
res = 0;
}
}
int main() {
cin.tie(0);
ios::sync_with_stdio(false);
cin >> n;
for (int i = 2; i <= n; i ++) {
int p; cin >> p;
g[p].push_back(i);
}
dfs(1, 0);
DFS(1, 0, false);
for (int i = 1; i <= n; i ++) cout << ans[i] << "\n";
return 0;
}
CF208E Blood Cousins
sol:
对于每一个询问
v
,
p
v,p
v,p,用倍增找到
v
v
v的
p
p
p级祖先
u
u
u,然后在以
u
u
u为根的子树中,与
v
v
v深度相同的节点个数
−
1
-1
−1即是
v
v
v的
p
p
p级表亲的个数。
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define all(x) begin(x),end(x)
#define debug(x) cout<<#x<<": "<<x<<endl;
using namespace std;
using ll = long long;
const int N = 1e5 + 10;
int n, m, limit, d[N];
vector<int> g[N];
int f[N][22];
int sz[N], L[N], R[N], son[N], node[N], totdfn;
vector<pair<int, int>> Q[N]; // Q[p-fa] : {v, id}
int ans[N], cnt[N]; // cnt[i] 当前深度为i的节点有多少
void dfs(int u) {
L[u] = ++totdfn; node[totdfn] = u;
sz[u] = 1; d[u] = d[f[u][0]] + 1;
for (int i = 1; i <= limit; i ++)
f[u][i] = f[f[u][i - 1]][i - 1];
for (auto& v: g[u]) {
if (v == f[u][0]) continue;
f[v][0] = u;
dfs(v);
sz[u] += sz[v];
if (!son[u] or sz[v] > sz[son[u]]) son[u] = v;
}
R[u] = totdfn;
}
void add(int u) {
cnt[d[u]] ++;
}
void del(int u) {
cnt[d[u]] --;
}
void DFS(int u, bool keep) {
for (auto& v: g[u]) {
if (v == f[u][0] or v == son[u]) continue;
DFS(v, false);
}
if (son[u]) DFS(son[u], true);
for (auto& v: g[u]) {
if (v == f[u][0] or v == son[u]) continue;
for (int i = L[v]; i <= R[v]; i ++) add(node[i]);
}
add(u);
for (auto& [v, id]: Q[u]) ans[id] = cnt[d[v]] - 1;
if (keep == false) {
for (int i = L[u]; i <= R[u]; i ++) del(node[i]);
}
}
int main() {
cin.tie(0);
ios::sync_with_stdio(false);
cin >> n;
limit = log2(n);
vector<int> rt;
for (int i = 1; i <= n; i ++) {
int p; cin >> p;
if (p == 0)rt.push_back(i);
else g[p].push_back(i);
}
for (auto& root: rt) dfs(root);
cin >> m;
for (int i = 1; i <= m; i ++) {
int v, p;
cin >> v >> p;
int u = v;
for (int j = limit; j >= 0; j --)
if (p >> j & 1) u = f[u][j];
Q[u].emplace_back(v, i);
}
for (auto& root: rt) DFS(root, false);
for (int i = 1; i <= m; i ++) cout << ans[i] << " \n"[i == m];
return 0;
}