Codeforces 371D - Vessels(思维 + 并查集)
题目大意:
第一行输入 n ,第二行输入n个数,表示第 i 个盘子的容积,第三行输入q,表示有q次询问,接下来输入q行,对于相应的询问输出答案,在往盘子中加水时,如果水量多于该盘子的容积,则剩余的水会继续流向下一个盘子,如果第n个盘子是满的,则会流到地上,浪费掉。
关于询问
- 1 x y 表示在第x个盘子 + y升的水
- 2 x 输出第x个盘子的水量
解题思路:
暴力模拟了几次,一直TLE 8 ,然后翻看了别人的题解,才想到这道题是一道并查集题,向第k个盘子注水时,实际上是对大于等于k的没装满水的盘子注水,所以如果第k个盘子注满水后,我们可以把k和 k + 1 个盘子合并起来,这样对k操作就是直接对大于等于k的盘子进行操作了。如果find(k) > n则退出循环。
Code:
#pragma GCC optimize(2)
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <cstring>
using namespace std;
const int N = 2e5 + 50;
const int inf = 0x3f3f3f3f;
int a[N], f[N], v[N];
int find(int x)
{
return f[x] == x ? x : f[x] = find(f[x]);
}
void merge(int x, int y)
{
int t1 = find(x);
int t2 = find(y);
f[t1] = t2;
}
int main()
{
ios::sync_with_stdio(false);
int n, q;
cin >> n;
for (int i = 1; i <= n; i ++)
cin >> v[i];
for (int i = 1; i <= n + 1; i ++)//这里要将n + 1个盘子也初始化,不然后面寻根的时候寻不到n + 1,因为这个点wa了几次
f[i] = i;
memset(a, 0, sizeof a);
cin >> q;
while (q--)
{
int op;
cin >> op;
if (op == 1)
{
int k, c;
cin >> k >> c;
while (c)
{
k = find(k);
if (k > n)
break;
if (a[k] + c >= v[k])//如果大于等于盘子的水量则证明该盘子能装满,将k, k + 1 合并起来
{
c -= (v[k] - a[k]);
a[k] = v[k];
merge(k, k + 1);
}
else
{
a[k] += c;
c = 0;
}
}
}
else
{
int k;
cin >> k;
cout << a[k] << endl;
}
}
return 0;
}