题目链接:Problem - 371D - Codeforces
题目大意:有n层容器用来装水, 当一层的水满了,就会向下溢出,进入下一层,最后一层的溢出将会在地上。现有两种操作 1.在p层的容器里加入x升水。 2.查询p层的水量为多少。
输入:
第一行包含整数 n --数量( 1 ≤ n ≤ 2·1e5 )。第二行包含 n 个整数 a1, a2, ..., an - 容量( 1 ≤ ai ≤ 1e9 )。从顶部到底部。第三行包含整数 m - 查询次数( 1 ≤ m ≤ 2·1e5 )。接下来的每行 m 包含一个查询的描述。第一类查询用" 1 pi xi "表示,第二类查询用" 2 ki "表示( 1 ≤ pi ≤ n , 1 ≤ xi ≤ 1e9 , 1 ≤ ki ≤ n )。
并查集
1.从题目可以看出,当水满了过后将会向下溢出,如果单纯的模拟水溢出的过程,很明显时间复杂度将会过高。
2.降低时间复杂度:通过模拟看出在当一个容器 i 满了过后, 如果在它上面的容器 i-1溢出了,可以避开容器 i 直接进入i+1容器的话,便可以降低时间复杂度。 当一个区间[L, R]里的所有容器都满了在L-1的容器溢出如果可以快速的找到第R+1个容器,那么时间复杂度将会减小的更多。
3.方法,并查集, 该算法的每个联通块都会维护一个根节点,如果将满了的[L,R]容器联通起来,维护一个最大的(也就是最下面的一层R层)。如果在L-1溢出,那么可以快速跳到 R 层。
注意:当满了过后 下一层即 p+1 , p <= n 即可限制住水溢出在地上。
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using i128 = __int128;
using ui64 = unsigned long long;
const int N = 2e5+9;
int tree[N];
i64 a[N], w[N];
int n;
//路径压缩,将满了的层数合并在一起
//注意此处让层数越大的作为根节点。
int find(int x) {
if(x != tree[x]) {
int fx = find(tree[x]);
tree[x] = fx;
}
return tree[x];
}
void go(int p, i64 x){
w[p] += x;//加水
while(p <= n && w[p] > a[p]) {
int fp = find(p);
tree[find(p-1)] = fp;//当下次倒水时在p-1层时,可以快速找到未满的一层
int ff = fp+1;//倒入下一层
w[ff] += w[p] - a[p];
w[p] = a[p];
p = ff;//检测下一层是否溢出
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n;
for(int i=1; i<=n; i++) {
cin >> a[i];
tree[i] = i;
}
int q;
cin >> q;
while(q--) {
int op;
cin >> op;
if(op==1) {
int p, x;
cin >> p >> x;
go(p, x);
}else{
int p;
cin >> p;
cout << w[p] << "\n";
}
}
return 0;
}
感谢你的收看与点赞, 欢迎大佬的指正。