开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第5天,点击查看活动详情
[传智杯 #5 练习赛] 树的变迁
题目描述
给定一棵具有 个节点的树,每个节点有一个初始权值 。
一共需要进行 次操作,每次操作包括:
-
1 e编号为 的边突然消失,使得它所在的那棵树变成了两棵树。 -
2 u val编号为 的节点的权值变成了 。 -
3 u进行了一次查询,查询 所在的那棵树的权值之和。
现在你需要来模拟上述事件,以了解树的变迁。
输入格式
第一行为 ,如上所述。
第二行有 个数,为 个结点的初始权值,在 以内。
下面 行,每行一组 ,表示一条边。(保证初始为一棵树)
下面 行有 个操作:
先读入一个 ,表示操作类型。
-
时,读入 ,表示删掉读入的第 条边。(保证第 条边存在)
-
时,读入 ,表示把结点 的权值改成 ()。
-
时,读入 ,表示查询 所在的那棵树的结点权值和。
输出格式
对于每个查询操作,输出一行一个数表示答案。
样例 #1
样例输入 #1
2 3
1 1
1 2
2 2 4
1 1
3 2
样例输出 #1
4
提示
所有测试数据数据满足 ,。
思路分析:
- 1.有删边但是没有恢复操作,我们可以存下所有的操作,然后进行逆序套路
- 2.先进行存储操作记录,记录一下没有被删除的边
- 3.用一个循环,将未被删除的边先连接起来
- 4.逆序操作
- 1.如果是1(删边)的话,根据逆序的操作,如果有查询在之前就已经查询过了,我们把这个边加进去。
- 2.如果是2(修改权值)的话,因为w[i]记录的是最后一次更改完的值,则我们取出栈中的值,当前值等于栈顶的值,sum加上当前值-w的值
- 3.如果是3(查询)的话,我们只要记录一下当前点的值就行了,但由于现在是逆序状态,所以不能现在输出,要再开一个循环逆序输出!
代码展示
#include<iostream>
#include<vector>
using namespace std;
const int N = 1e5 + 10;
vector<int>v[N];//点,记录点的逆序权值
int w[N];//权重
int pre[N];//前驱结点
int sum[N];//树和
int st[N];//记录边是否会被删除
int res[N];//记录结果
//边
struct edge {
int a;
int b;
}e[N];
//操作记录
struct option {
int u;
int op;//操作的种类
}opt[N];
//查找函数
int find(int x) {
return x == pre[x] ? x : pre[x] = find(pre[x]);
}
//合并函数
void join(int x, int y) {
int fx = find(x);
int fy = find(y);
if (fx != fy) {
pre[fx] = fy;
sum[fy] += sum[fx];
sum[fx] = 0;
}
}
int n, m;
int main()
{
cin >> n >> m;
//存入权值
for (int i = 1; i <= n; i++)cin >> w[i];
//存入边
for (int i = 1; i < n; i++)cin >> e[i].a >> e[i].b;
//存入操作
for (int i = 1; i <= m; i++) {
cin >> opt[i].op;
if (opt[i].op == 1) {
cin >> opt[i].u;
st[opt[i].u] = 1;//记录
}
else if (opt[i].op == 2) {
cin >> opt[i].u;
int u = opt[i].u;
int value;
cin >> value;
v[u].push_back(w[u]);
w[u] = value;
}
else {
cin >> opt[i].u;
}
}
//逆序初始化
for (int i = 1; i <= n; i++) {
sum[i] = w[i];
pre[i] = i;
}
//对于没删除的边进行合并
for (int i = 1; i < n; i++) {
if (!st[i])
join(e[i].a, e[i].b);
}
int cnt = 0;
//逆序处理
for (int i = m; i > 0; i--) {
if (opt[i].op == 1) {
join(e[opt[i].u].a, e[opt[i].u].b);
}
else if (opt[i].op == 2) {
int u = opt[i].u;
int nowValue = v[u][v[u].size() - 1];
v[u].pop_back();
sum[find(u)] += nowValue - w[u];
w[u] = nowValue;
}
else if (opt[i].op == 3) {
res[++cnt] = sum[find(opt[i].u)];
}
}
for (int i = cnt; i > 0; i--) {
cout << res[i] << endl;
}
return 0;
}