给定一棵具有 \(n\) 个节点的树,每个节点有一个初始权值 \(a_i\)。 观察我们需要进行的三种操作,操作2,3其实相对容易进行处理,最难处理的就是对一棵树进行分裂并统计子树的权值之和,所以我们不妨换个思路,倒着想: 思路理清楚之后就是考虑怎么维护这些操作了,因为基本只涉及到了集合的合并,所以很容易想到用并查集来维护T292115 [传智杯 #5 练习赛] 树的变迁
题目大意:
一共需要进行 \(m\) 次操作,每次操作包括:
1.1 e
编号为 \(e\) 的边突然消失,使得它所在的那棵树变成了两棵树。
2.2 u val
编号为 \(u\) 的节点的权值变成了 \(val\)。
3.3 u
进行了一次查询,查询 \(u\) 所在的那棵树的权值之和。
现在你需要来模拟上述事件,以了解树的变迁。
解题思路:
考虑当所有操作结束以后,逆向进行操作,因为操作已经结束,所以分裂完的子树内部是相互连接的,所以整个图就变成了多个集合,假如我们要倒着从最后一个操作逆向回到初始状态,那么就等同于将原本分裂的各个子树集合合并起来并更新权值,这对我们来说是容易维护子树权值的(合并时同时合并子树权值)
此时对子树的权值的更新(操作2)就等同于维护a[find(u)] += (last- now),权值变换所带来影响
代码实现:
#include<bits/stdc++.h>
using namespace std;
# define int long long
# define endl "\n"
# define mk(a,b) make_pair(a,b)
const int N = 2e5 + 10, inf = 0x3f3f3f3f;
typedef pair<int,int> pii;
struct node{
int a,b;
} edg[N];
int a[N];//最终的权值
int vis[N];//正向所有操作完成后第i条边是否联通
int f[N];
vector<int> cal[N];//权值变动
vector<pii> ans;//操作的集合
int find(int x){
if(x == f[x]) return x;
return f[x] = find(f[x]);
}
void solve() {
int n,m;
cin>>n>>m;
for(int i = 1;i <= n;++i) {//读入权值
f[i] = i;
cin>>a[i];
cal[i].push_back(a[i]);
vis[i] = 0;
}
for(int i = 1;i < n;++i){//读入边
int x,y;
cin>>x>>y;
edg[i] = {x,y};
}
while(m--){
int op,x;
cin>>op>>x;
if(op == 1){
vis[x] = 1;
}
if(op == 2){
cin>>a[x];//节点x的权值变化为
cal[x].push_back(a[x]);// 保存节点x每次变化后的权值,方便之后维护对子树权值之和的影响
}
ans.push_back(mk(op,x));
}
for(int i = 1;i < n;++i){
if(!vis[i]){//将正向操作最终状态合并出来
int x = edg[i].a,y = edg[i].b;
int fx = find(x),fy = find(y);
f[fx] = fy;
a[fy] += a[fx];
}
}
reverse(ans.begin(),ans.end());//逆向操作
vector<int> p;//逆向答案集合
for(auto [op,x]:ans){
if(op == 1){//分裂改为合并
int xx = edg[x].a,yy = edg[x].b;
int fx = find(xx),fy = find(yy);
f[fx] = fy;
a[fy] += a[fx];
}
if(op == 2){//修改权值
int last = cal[x].back();//last表示节点x的最终状态
cal[x].pop_back();
int now = cal[x].back();//now表示逆向修改后的状态
a[find(x)]+=(now-last);//对整个子树的影响就是now-last
}
if(op == 3){
p.push_back(a[find(x)]);
}
}
reverse(p.begin(),p.end());//倒转回正向输出结果
for(auto v:p) cout<<v<<endl;
}
int tt;
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
tt = 1;
// cin >> tt;
while (tt--) solve();
return 0;
}