Skip to main content
  1. Posts/

CodeForces-371D Vessels

·2 mins·

CodeForces-371D Vessels #

题目大意 #

有 $n$ 个从上到下摆放的容器, 每个容器的容量为 $a_i$ . 一个容器中溢出的水会流到下一个容器中, 最后一个容器的水则会流到地上. 有 $m$ 次倒水和查询的操作, 输出结果.

Solution 1 #

我们需要快速地找到每个容器后面一个可行的容器是什么, 注意到连续的盛满水的容器可以看作一组, 我们可以使用并查集来记录. 记 $pa[i] + 1$ 为 $i$ 之后第一个非空的容器. 每次加水, 如果 $i$ 满了, 就将 $i - 1$ 的父节点设为 $i$ 的父节点( $i$ 上面的容器一定会流到 $i - 1$ , $i - 1$ 也会先流到 $i$), 同时继续考虑 $i = pa[i] + 1$ . 这种操作下满了的容器不会再被考虑到, 总体复杂度为 $O(m + n)$ . 代码如下:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll MOD = 1e9 + 7;
int pa[200010];

void init(int n){
    for(int i = 1; i <= n + 1; i++){
        pa[i] = i;        //存放每个结点的结点(或双亲结点)
    }
}

int find(int i) {
    if (pa[i] == i) {
        return i;
    }
    else {
        pa[i] = find(pa[i]);
        return pa[i];
    }
}

void merge(int i, int j) {
    pa[find(j)] = find(i);
}

int main() {
    int n;
    cin>>n;
    vector<ll> a(n + 2, 0), b(n + 2, 0);
    for (int i = 1; i <= n; i++) {
        cin>>a[i];
    }
    int m;
    cin>>m;
    init(n);
    while (m--) {
        int type;
        cin>>type;
        if (type == 1) {
            int p, x;
            cin>>p>>x;
            b[p] += x;
            while (p <= n && b[p] > a[p]) {
                merge(p, p - 1);
                int np = find(p) + 1;
                b[np] += b[p] - a[p];
                b[p] = a[p];
                p = np;
            }
        }
        else {
            int k;
            cin>>k;
            cout<<b[k]<<endl;
        }
    }
    return 0;
}