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;
}	
posted @ 2020-08-01 11:16  Hayasaka  阅读(49)  评论(0编辑  收藏  举报