我来了,我来了,我来了,啊啊啊啊啊啊啊啊
我A了,来了, 我太激动了
题目
给定一棵 n个节点的树,初始时该树的根为1号节点,每个节点有一个给定的权值。下面依次进行m个操作,操作分为如下五种类型:
❶换根:将一个指定的节点设置为树的新根。
❷修改路径权值:给定两个节点,将这两个节点间路径上的所有节点权值(含这两个节点)增加一个给定的值。
❸修改子树权值:给定一个节点,将以该节点为根的子树内的所有节点权值增加一个给定的值。
❹询问路径:询问某条路径上节点的权值和。
❺询问子树:询问某个子树内节点的权值和。
输入格式
第一行一个整数 n,表示节点的个数。
第二行n个整数表示第 i 个节点的初始权值 。
第三行n-1个整数,表示 i+1 号节点的父节点编号 。
第四行一个整数 m,表示操作个数。
接下来 m 行,每行第一个整数表示操作类型编号:
❶若类型为 1,则接下来一个整数 u,表示新根的编号。
❷若类型为 2,则接下来三个整数u,v,k分别表示路径两端的节点编号以及增加的权值。
❸若类型为 3,则接下来两个整数 u,k分别表示子树根节点编号以及增加的权值。
❹若类型为 4,则接下来两个整数 u,v表示路径两端的节点编号。
❺若类型为 5,则接下来一个整数 u,表示子树根节点编号。
输出格式
对于每一个类型为 4 或 5 的操作,输出一行一个整数表示答案。
样例
样例输入
6
1 2 3 4 5 6
1 2 1 4 4
6
4 5 6
2 2 4 1
5 1
1 4
3 1 2
4 2 5
样例输出
15
24
19
额外赠送数据一个:
6
1 2 3 4 5 6
1 2 1 4 4
1
1 6
5 4
输出:
15
数据范围与提示
对于100% 的数据,1≤n,m,k,ai≤105。数据有一定梯度。
题解
这道题也是一道树链剖分的模板,不了解的亲故可移步
树链剖分入门
其实这道题的2,3,4,5类型的操作都是模板可以直接使用
but唯一的难点就是换根这个操作
相信很多朋友都会想到每次遇到1操作,就重新建树
用脚想都知道这么建下去,编译器就要被加班卡死了
所以我们必须找到一个nb办法——不换根
只是每次遇到1操作,就把root赋成u即可
我们考虑其实以任何点为根,对路径的更改和查询是不会影响的
因为这是一棵树,两点之间的路径是唯一的,永远都是那么走,
至于根在哪?
那么对于子树的查询和修改就比较
有两种情况
Ⅰ:当前的根root未被u包含,
也就是我们以1为根初建树时,root就不属于u的子树
那么易知此时的root不会影响有关u的子树操作
即u原本子树包含哪些,假设重新建树后也不会有所变换
此时就按照模板去查询或更改即可
Ⅱ:根root在u的子数范围内
这种情况就有点南翔了
这个时候我们就要搞懂dfs建树时的走向
经过分析你会发现,root成为根之后,它所在的那一条链都将脱离u的殖民统治,成为一个独立自主的新链或新子树
那么此时我们只需要暴力(不会TLE )去爬链,
找到root所在u子树中的这条链,再减去即可完成
最后有几点需要注意的;
1)当u==root的时候,特别处理一下,就是query(1,n)
不然会RE
2)一定要记得开long long,
特别是query的左右子树查询用临时变量sum储存的地方,我错了十次
3)有的亲故可能会用lca去判断u,root是否在一个子树内
其实不必,因为我们强大的dfs保证了i点的子树id编号一定是连续的
所以你直接用id进行比较即可
代码实现
#include <cstdio>
#include <vector>
using namespace std;
#define MAXN 100005
#define LL long long
vector < int > G[MAXN];
int n, m, cnt;
int a[MAXN];
int f[MAXN], id[MAXN], top[MAXN], dep[MAXN], son[MAXN], tot[MAXN];
LL tree[MAXN << 2], lazy[MAXN << 2];
void dfs1 ( int u, int fa, int depth ) {
dep[u] = depth;
tot[u] = 1;
for ( int i = 0;i < G[u].size();i ++ ) {
int v = G[u][i];
if ( v == fa ) continue;
dfs1 ( v, u, depth + 1 );
tot[u] += tot[v];
if ( tot[v] > tot[son[u]] || ! son[u] )
son[u] = v;
}
}
void dfs2 ( int u, int t ) {
id[u] = ++ cnt;
top[u] = t;
if ( ! son[u] ) return;
dfs2 ( son[u], t );
for ( int i = 0;i < G[u].size();i ++ ) {
int v = G[u][i];
if ( v == son[u] || v == f[u] ) continue;
dfs2 ( v, v );
}
}
void pushdown ( int l, int r, int num ) {
int mid = ( l + r ) >> 1;
tree[num << 1] += lazy[num] * ( mid - l + 1 );
tree[num << 1 | 1] += lazy[num] * ( r - mid );
lazy[num << 1] += lazy[num];
lazy[num << 1 | 1] += lazy[num];
lazy[num] = 0;
}
void update ( int l, int r, int L, int R, int num, LL val ) {
if ( L <= l && r <= R ) {
tree[num] += val * ( r - l + 1 );
lazy[num] += val;
return;
}
pushdown ( l, r, num );
int mid = ( l + r ) >> 1;
if ( L <= mid ) update ( l, mid, L, R, num << 1, val );
if ( mid < R ) update ( mid + 1, r, L, R, num << 1 | 1, val );
tree[num] = tree[num << 1] + tree[num << 1 | 1];
}
LL query ( int l, int r, int L, int R, int num ) {
if ( L <= l && r <= R ) return tree[num];
pushdown ( l, r, num );
int mid = ( l + r ) >> 1;
LL sum1 = 0, sum2 = 0;
if ( L <= mid ) sum1 = query ( l, mid, L, R, num << 1 );
if ( mid < R ) sum2 = query ( mid + 1, r, L, R, num << 1 | 1 );
return sum1 + sum2;
}
void solve_sum ( int x, int y, bool flag, LL val ) {
LL ans = 0;
int fx = top[x], fy = top[y];
while ( fx != fy ) {
if ( dep[fx] >= dep[fy] ) {
if ( flag ) ans += query ( 1, cnt, id[fx], id[x], 1 );
else update ( 1, cnt, id[fx], id[x], 1, val );
x = f[fx];
fx = top[x];
}
else {
if ( flag ) ans += query ( 1, cnt, id[fy], id[y], 1 );
else update ( 1, cnt, id[fy], id[y], 1, val );
y = f[fy];
fy = top[y];
}
}
if ( id[x] <= id[y] ) {
if ( flag ) ans += query ( 1, cnt, id[x], id[y], 1 );
else update ( 1, cnt, id[x], id[y], 1, val );
}
else {
if ( flag ) ans += query ( 1, cnt, id[y], id[x], 1 );
else update ( 1, cnt, id[y], id[x], 1, val );
}
if ( flag )
printf ( "%lld\n", ans );
}
int find_top ( int u, int t ) {
while ( f[u] != t )
u = f[u];
return u;
}
int main() {
scanf ( "%d", &n );
for ( int i = 1;i <= n;i ++ )
scanf ( "%d", &a[i] );
for ( int i = 1;i < n;i ++ ) {
scanf ( "%d", &f[i + 1] );
G[f[i + 1]].push_back ( i + 1 );
}
dfs1 ( 1, 0, 1 );
dfs2 ( 1, 1 );
for ( int i = 1;i <= n;i ++ )
update ( 1, cnt, id[i], id[i], 1, a[i] );
int root = 1;
scanf ( "%d", &m );
for ( int i = 1;i <= m;i ++ ) {
int opt, u, v;
LL k;
scanf ( "%d", &opt );
if ( opt == 1 ) {
scanf ( "%d", &u );
root = u;
}
if ( opt == 2 ) {
scanf ( "%d %d %lld", &u, &v, &k );
solve_sum ( u, v, 0, k );
}
if ( opt == 3 ) {
scanf ( "%d %lld", &u, &k );
if ( root == u )
update ( 1, cnt, 1, n, 1, k );
else {
if ( id[root] < id[u] || id[root] > id[u] + tot[u] - 1 )
update ( 1, cnt, id[u], id[u] + tot[u] - 1, 1, k );
else {
int son = find_top ( root, u );
update ( 1, cnt, 1, n, 1, k );
update ( 1, cnt, id[son], id[son] + tot[son] - 1, 1, -k );
}
}
}
if ( opt == 4 ) {
scanf ( "%d %d", &u, &v );
solve_sum ( u, v, 1, 0 );
}
if ( opt == 5 ) {
scanf ( "%d", &u );
if ( root == u )
printf ( "%lld\n", query ( 1, cnt, 1, n, 1 ) );
else {
if ( id[root] < id[u] || id[root] > id[u] + tot[u] - 1 )
printf ( "%lld\n", query ( 1, cnt, id[u], id[u] + tot[u] - 1, 1 ) );
else {
int son = find_top ( root, u );
printf ( "%lld\n", query ( 1, cnt, 1, n, 1 ) - query ( 1, cnt, id[son], id[son] + tot[son] - 1, 1 ) );
}
}
}
}
return 0;
}
这道题卡了本仙女将近两个星期,唉
好了,有什么问题或不懂的欢迎评论,让我们满怀新激情去跳下一个坑!