T292115 [传智杯 #5 练习赛] 树的变迁(并查集+倒序操作处理树分裂)
T292115 [传智杯 #5 练习赛] 树的变迁
题目大意:
给定一棵具有 个节点的树,每个节点有一个初始权值 。
一共需要进行 次操作,每次操作包括:
1.1 e
编号为 的边突然消失,使得它所在的那棵树变成了两棵树。
2.2 u val
编号为 的节点的权值变成了 。
3.3 u
进行了一次查询,查询 所在的那棵树的权值之和。
现在你需要来模拟上述事件,以了解树的变迁。
解题思路:
观察我们需要进行的三种操作,操作2,3其实相对容易进行处理,最难处理的就是对一棵树进行分裂并统计子树的权值之和,所以我们不妨换个思路,倒着想:
考虑当所有操作结束以后,逆向进行操作,因为操作已经结束,所以分裂完的子树内部是相互连接的,所以整个图就变成了多个集合,假如我们要倒着从最后一个操作逆向回到初始状态,那么就等同于将原本分裂的各个子树集合合并起来并更新权值,这对我们来说是容易维护子树权值的(合并时同时合并子树权值)
此时对子树的权值的更新(操作2)就等同于维护a[find(u)] += (now-last),权值变换所带来影响
思路理清楚之后就是考虑怎么维护这些操作了,因为基本只涉及到了集合的合并,所以很容易想到用并查集来维护
代码实现:
#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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】