题目描述:
Vessels
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output
There is a system of n vessels arranged one above the other as shown in the figure below. Assume that the vessels are numbered from 1 to n, in the order from the highest to the lowest, the volume of the i-th vessel is *a**i* liters.
Initially, all the vessels are empty. In some vessels water is poured. All the water that overflows from the i-th vessel goes to the (i + 1)-th one. The liquid that overflows from the n-th vessel spills on the floor.
Your task is to simulate pouring water into the vessels. To do this, you will need to handle two types of queries:
- Add *x**i* liters of water to the *p**i*-th vessel;
- Print the number of liters of water in the *k**i*-th vessel.
When you reply to the second request you can assume that all the water poured up to this point, has already overflown between the vessels.
Input
The first line contains integer n — the number of vessels (1 ≤ n ≤ 2·105). The second line contains n integers a1, a2, ..., *a**n* — the vessels' capacities (1 ≤ *a**i ≤ 109). The vessels' capacities do not necessarily increase from the top vessels to the bottom ones (see the second sample). The third line contains integer m* — the number of queries (1 ≤ m ≤ 2·105). Each of the next m lines contains the description of one query. The query of the first type is represented as "1 *p**i* xi", the query of the second type is represented as "2 ki" (1 ≤ pi ≤ n, 1 ≤ xi ≤ 109, 1 ≤ *k**i ≤ n*).
Output
For each query, print on a single line the number of liters of water in the corresponding vessel.
Examples
Input
Copy
25 1061 1 42 11 2 51 1 42 12 2
Output
Copy
4
5
8
Input
Copy
35 10 861 1 122 21 1 61 3 22 22 3
Output
Copy
7
10
5
思路:
这道题是让模拟一下水流的过程,不断操作,或者往一个槽子加水,或者询问某个槽子的水量。
首先一个最最朴实的想法是:建立数组模拟水流过程,若水满了,往下一个槽子流即可。但是在测试点8就回超时。显然还有优化的余地。
怎么优化呢?然后就开始了冥思苦想的过程,想到往一个槽子里的水加满后,实际上以后再往这个槽子里加水就可以忽略这个槽子了,不只是这个,由于槽子中的水不会减少,水流过程中的满的槽子均可以忽略。怎么实现呢?我首先想到了链表,不断可以摘除满的节点,从而使遍历更快捷。但是如果这样该怎么跟槽子的编号对应呢?比如我1到5号,其中3号已满,我删了之后以后如果再从3号倒水我怎么知道他该从哪个槽子开始倒呢?
又想了想前缀和。我们可以维护一个水流槽子的前缀和,比如样例1,有5,10,8,前缀和是5,15,23,只有当流过1的水流大于5才会流到2号槽子中,大于15才会流到3号槽子中,大于23才会往后面的槽子流。就累计一下从1开始流的总水量,下一次如果是从1开始流的话我们就立刻在1的前缀和中二分查找(单调性)到接下来该往哪个槽子流。这样一想,岂不是要维护2*10^5个前缀和,而且由于每次pour的起点不一样,维护操作可能会十分复杂。
其实最终的思路和思路一是一致的,区别在于实现上。实际上我们并不需要在物理上删除这个节点,只要在逻辑上跳过它就行了。我们建立一个nxt数组,来实现逻辑上的跳转。每次pour都把已满的槽子的nxt最终指向最近的未满槽子。那么每次再从满的槽子pour的时候不用一个一个遍历,而直接跳转到未满的下一个槽子了。
代码:
郁闷,非常郁闷,我把最终循环出来的起点设置为pour起点p的前面一个位置就一直T38,改为起点p就AC了。为什么嘞?因为啊,如果p出来是1,nxt[p]本来是2,nxt[p-1]=nxt[0],这个值没有意义,也没有设置过,就是默认的0,于是,t=nxt[0],循环里面nxt[0]=i;j=t=0;也就是一直都是t=0在无限循环,直到超时。我原先的想法是,既然p这个位置满了,那它之前的下次流出的时候的nxt也应该跳转,可以节省一次循环,但由于忘记考虑特殊的0点,(其实是考虑了,但是觉得没错,(:з」∠)),就陷入了死循环,如果把nxt[0]改为1应该就好了。注意考虑边界条件。
#include <iostream>
#include <cstdio>
#define max_n 200005
using namespace std;
int n,m;
long long a[max_n];
long long b[max_n];
long long nxt[max_n];
template <typename T>
inline void read(T& x)
{
x=0;int f = 0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=getchar();}
while('0'<=ch&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
x=f?-x:x;
}
#pragma optimize(2)
int main()
{
read(n);
for(int i = 1; i<=n; i++)
{
read(a[i]);
nxt[i] = i+1;
}
nxt[n+1]=n+1;
read(m);
while(m--)
{
int st;
read(st);
switch(st)
{
case 1:
{
int p;
long long x;
read(p);
read(x);
int i;
long long sum = 0;
for(i = p; i<=n; i=nxt[i])
{
//cout << "x " << x << " i " << i << " a[i] " << a[i] << " b[i] " << b[i] << endl;
if(b[i]+x<=a[i])
{
b[i] += x;
break;
}
sum = x+b[i]-a[i];
b[i] = a[i];
x = sum;
}
int t;
if(a[i]==b[i])i=nxt[i];
for(int j = p; j<i; j=t)//p改为p-1就T了
{
t = nxt[j];
nxt[j] = i;
}
/*cout << "next ";
for(int i = 1;i<=n;i++)
{
cout << nxt[i] << " ";
}
cout << endl;*/
break;
}
case 2:
int k;
read(k);
printf("%I64d\n",b[k]);
break;
}
}
return 0;
}
参考文章:
码代码的猿猿的AC之路,CodeForces 371D. Vessels,https://blog.csdn.net/ck_boss/article/details/17249213