第二十届浙大城市学院程序设计竞赛 I.Magic Tree DFS序线段树
解答方法:
大致思路:
我们知道dfs序上的整颗子树dfs序编号连续,因为每次删除一个点或者新增一个点都导致子树上所有点的深度加一或者减一。由于是区间修改所以我们考虑dfs序上建线段树。
我们会发现直接建树后面插入边的时候很难处理,所以我们直接把所有的边加进来再进行dfs序处理。
如何处理之前连过的边:
因为给定的操作会往两个节点中间加点,我们知道无论是链式前向星还是vector存图,每次真的去删除边时间复杂度是很高的,我们可以使用一个名为fa的数组来记录每个结点被更新之后的父亲是谁,然后dfs到u结点的时候,对于边v,判断fa[v] == u是否成立即可。
如何处理删除操作
删除结点x的时候需要更新结点x的儿子的父亲,所以再维护一个son数组,动态维护每个结点的儿子即可。
解题思路:
由于子树的dfs序连续,所以我们修改的时候是需要修改dfs序连续的一段,所以我们建树的时候需要按照dfs序建树才是正确的。我们在进行第一个操作的时候,只需要算出给定结点的父节点的深度,然后增加的结点的深度就是父节点的深度加1,我们进行单点修改,因为新增加了结点,所以给子树上所有的点加1。然后更新一下son和fa数组即可。
#include <iostream>
#include <cstring>
#include <iomanip>
#include <algorithm>
#include <stack>
#include <queue>
#include <numeric>
#include <cassert>
#include <bitset>
#include <cstdio>
#include <vector>
#include <unordered_set>
#include <cmath>
#include <map>
#include <unordered_map>
#include <set>
#include <deque>
#include <tuple>
#include <array>
#define all(a) a.begin(), a.end()
#define cnt0(x) __builtin_ctz(x)
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define cntone(x) __builtin_popcount(x)
#define db double
#define fs first
#define se second
#define AC main(void)
#define ls u << 1
#define rs u << 1 | 1
typedef std::pair<int, int> PII;
#define HYS std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
const long double eps = 1e-9;
int MOD = 571373;
const int N = 4e5 + 10, M = 6e5 + 10;
const int INF = 0x3f3f3f3f;
ll min(ll a, ll b) {return a > b ? b : a;}
ll max(ll a, ll b) {return a > b ? a : b;}
int min(int a, int b) {return a > b ? b : a;}
int max(int a, int b) {return a > b ? a : b;}
int n , m, _;
int d1[] = {0, 0, 1, -1};
int d2[] = {1, -1, 0, 0};
int id[N], cnt;
int dep[N], sz[N], top[N], rid[N];
int fa[N];
int son[N];
std::vector<int> G[N];
struct node{
int v;
}tr[N << 2];
inline void build(int u, int l, int r){
if(l == r){
tr[u].v = dep[rid[l]];
return ;
}
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
}
inline void pushdown(int u){
if(tr[u].v){
int x = tr[u].v;
tr[ls].v += x;
tr[rs].v += x;
tr[u].v = 0;
}
}
inline int query(int u, int L, int R, int x){
if(L == R) return tr[u].v;
pushdown(u);
int mid = L + R >> 1;
if(x <= mid) return query(u << 1, L, mid, x);
return query(u << 1 | 1, mid + 1, R, x);
}
inline void modify(int u, int L, int R, int l, int r, int x){
if(L == R){
tr[u].v += x;
return ;
}
if(L >= l && R <= r){
tr[u].v += x;
return ;
}
pushdown(u);
int mid = L + R >> 1;
if(l <= mid) modify(u << 1, L, mid, l, r, x);
if(r > mid) modify(u << 1 | 1, mid + 1, R, l, r, x);
}
inline void ADD(int u, int L, int R, int x, int v){
if(L == R){
tr[u].v = v;
return ;
}
pushdown(u);
int mid = L + R >> 1;
if(x <= mid) ADD(u << 1, L, mid, x, v);
else ADD(u << 1 | 1, mid + 1, R, x, v);
}
inline void dfs(int u, int depth){
id[u] = ++ cnt, rid[cnt] = u, dep[u] = depth, sz[u] = 1;
for (auto &v : G[u]){
if (fa[v] != u) continue;
if(v <= n)
dfs(v, depth + 1);
else dfs(v, depth);
sz[u] += sz[v];
}
}
inline void solve(){
std::cin >> n >> m;
std::vector<int> A(n + 1);
for(int i = 2; i <= n; i ++){
int x;
std::cin >> x;
fa[i] = x;
G[x].push_back(i);
A[i] = x;
}
std::vector<PII> g(m + 1);
for(int i = 1; i <= m; i ++){
int op, x;
std::cin >> op >> x;
g[i] = {op, x};
if(op == 1){
int father = fa[x];
fa[x] = i + n;
fa[i + n] = father;
G[i + n].push_back(x);
G[father].push_back(i + n);
}
}
dfs(1, 1);
int szz = n + m;
build(1, 1, szz);
for(int i = 2; i <= szz; i ++){
if(i <= n){
fa[i] = A[i];
son[A[i]] = i;
}else fa[i] = son[i] = 0;
}
for(int i = 1; i <= m; i ++){
auto &[op, x] = g[i];
if(op == 1){
int father = fa[x];
modify(1, 1, szz, id[x], id[x] + sz[x] - 1, 1);
fa[i + n] = father;
fa[x] = i + n;
son[father] = n + i;
son[n + i] = x;
int depth = query(1, 1, szz, id[father]);
ADD(1, 1, szz, id[i + n], depth + 1);
}else if(op == 2){
int father = fa[x];
int sn = son[x];
fa[sn] = father;
son[father] = sn;
modify(1, 1, szz, id[x], id[x] + sz[x] - 1, -1);
}else{
std::cout << query(1, 1, szz, id[x]) << '\n';
}
}
}
int main(){
HYS
_ = 1;
while(_ --)
solve();
return 0;
}