题干:

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 ai 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:

  1. Addxiliters of water to thepi-th vessel;
  2. Print the number of liters of water in theki-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, ..., an — the vessels' capacities (1 ≤ ai ≤ 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 pi xi", the query of the second type is represented as "2 ki" (1 ≤ pi ≤ n, 1 ≤ xi ≤ 109, 1 ≤ ki ≤ n).

Output

For each query, print on a single line the number of liters of water in the corresponding vessel.

Examples

Input

2
5 10
6
1 1 4
2 1
1 2 5
1 1 4
2 1
2 2

Output

4
5
8

Input

3
5 10 8
6
1 1 12
2 2
1 1 6
1 3 2
2 2
2 3

Output

7
10
5

解题报告:

    先来一发暴力TLE,然后发现可以用并查集的区间合并。

TLE代码:

#include<bits/stdc++.h>

using namespace std;
const int MAX = 2e5 + 5;
int a[MAX],rest[MAX],ne[MAX];

int main()
{
int n,m,x,y,op;
cin>>n;
for(int i = 1; i<=n; i++) scanf("%d",a+i),rest[i]=a[i],ne[i]=i;
cin>>m;
while(m--) {
scanf("%d",&op);
if(op == 2) {
scanf("%d",&x);
printf("%d\n",a[x] - rest[x]);
}
else {
scanf("%d%d",&x,&y);
while(rest[ne[x]] == 0 && ne[x] <= n) ne[x]++;//ne[x]=ne[x+1];
while(y>0 && ne[x]<=n) {
if(y>=rest[ne[x]]) {
y-=rest[ne[x]];
rest[ne[x]]=0;
ne[x]++;
}
else rest[ne[x]]-=y,y=0;
}
}
}
return 0 ;
}
/*
in:
10
71 59 88 55 18 98 38 73 53 58
20
1 5 93
1 7 69
2 3
1 1 20
2 1
2 10
1 6 74
1 7 100
1 9 14
2 3
2 4
2 7
1 3 31
2 4
1 6 64
2 2
2 2
1 3 54
2 9
2 1
1 6 86
out:
0
0
0
0
38
0
0
0
53
20

*/
#include<bits/stdc++.h>

using namespace std;
const int MAX = 2e5 + 5;
int a[MAX],rest[MAX],ne[MAX];

int main()
{
int n,m,x,y,op;
cin>>n;
for(int i = 1; i<=n; i++) scanf("%d",a+i),rest[i]=a[i],ne[i]=i;
ne[n+1] = n+1;
cin>>m;
while(m--) {
scanf("%d",&op);
if(op == 2) {
scanf("%d",&x);
printf("%d\n",a[x] - rest[x]);
}
else {
scanf("%d%d",&x,&y);
while(rest[ne[x]] == 0 && ne[x] <= n) {
if(ne[x]!=ne[x+1])ne[x]=ne[x+1];
else ne[x]++,ne[x+1]++;
}
while(y>0 && ne[x]<=n) {
if(y>=rest[ne[x]]) {
y-=rest[ne[x]];
rest[ne[x]]=0;
//ne[x]++;
//ne[x]=ne[ne[x+1]];
if(ne[x]!=ne[x+1])ne[x]=ne[x+1];
else ne[x]++,ne[x+1]++;
}
else rest[ne[x]]-=y,y=0;
}
}
}
return 0 ;
}

后来发现可以ne[x]=ne[ne[x+1]],然后就可以ne[x]=ne[ne[ne[x+1]]]以此类推,再仔细一看,这不就是并查集的getf函数的过程吗。

开搞:

AC代码:

#include<bits/stdc++.h>

using namespace std;
const int MAX = 2e5 + 5;
int a[MAX],rest[MAX],f[MAX];
int getf(int v) {
return f[v] == v ? v : f[v] = getf(f[v]);
}
void merge(int u,int v) {//默认u < v
int t1 = getf(u);
int t2 = getf(v);
if(t1 != t2) f[t1] = t2;
}
int main()
{
int n,m,x,y,op;
cin>>n;
for(int i = 1; i<=n; i++) scanf("%d",a+i),rest[i]=a[i],f[i]=i;
f[n+1] = n+1;
cin>>m;
while(m--) {
scanf("%d",&op);
if(op == 2) {
scanf("%d",&x);
printf("%d\n",a[x] - rest[x]);
}
else {
scanf("%d%d",&x,&y);
int t1=getf(x);
while(y>0 && t1 <= n) {
t1 = getf(t1);
if(y>=rest[t1] && t1 <= n) {//只能装rest[t1]的水了
y-=rest[t1];
rest[t1]=0;
merge(t1,t1+1);
}
else rest[t1]-=y,y=0;
}
}
}
return 0 ;
}

别忘处理边界啊,就是已经到最下面那个瓶子哪里了,所以两个地方都需要t1<=n,给个样例:

10
71 59 88 55 18 98 38 73 53 58
8
1 5 93
1 7 69
1 1 20
1 6 74
1 7 100
1 9 14
1 6 64
2 2

本该是1,却输出13吗,这其实是一个越界的值,这里的越界不是因为无限递归到2e5+5,而是在merge(10,11)的时候,因为f[11]没有初始化,所以f[11]=0,所以不会报错!程序也不会进入死循环但是相当于f[..6,7,8,9,10]这些本该=10的,都变为了=0;所以覆盖了之前的结果!

解决方法也有很多,

1.比如在if中也加上t1<=n。

2.或者初始化的时候for(int i = 1; i<MAX; i++) f[i]=i;而不是只初始化到n,这样就会在while中给break掉。所以思考问题时要注意整个程序可能用到的下标!而不是正解要用到的下标,或者说对于输入数据的合法的下标! 

3.还是针对初始化,加上rest[n+1] = INT_MAX;

     即:一般数组模拟问题都需要初始化一下边界值,比如从i=1开始读入数组,那就需要更新一下a[0],a[n+1],防止越界!这种题目太多太多,在此不再赘述。总之数组模拟真实情况的场景,最好都处理一下边界。不然就可能发生各种无法解释的脑残事件。

 

但是针对2和3这两种方法,会TLE8,不知道为什么、、、

初始化的时候把rest改成全都INT_MAX,也可以AC,,奇怪了

AC代码:

#include<bits/stdc++.h>

using namespace std;
const int MAX = 2e5 + 5;
int a[MAX],rest[MAX],f[MAX];
int getf(int v) {
return f[v] == v ? v : f[v] = getf(f[v]);
}
void merge(int u,int v) {//默认u < v
int t1 = getf(u);
int t2 = getf(v);
if(t1 != t2) f[t1] = t2;
}
int main()
{
int n,m,x,y,op;
cin>>n;
for(int i = 1; i<MAX; i++) f[i]=i,rest[i]=INT_MAX;
for(int i = 1; i<=n; i++) scanf("%d",a+i),rest[i]=a[i],f[i]=i;
rest[n+1] = INT_MAX;
f[n+1] = n+1;
cin>>m;
while(m--) {
scanf("%d",&op);
if(op == 2) {
scanf("%d",&x);
printf("%d\n",a[x] - rest[x]);
}
else {
scanf("%d%d",&x,&y);
int t1=getf(x);
while(y>0 && t1 <= n) {
t1 = getf(t1);
if(y>=rest[t1]) {//只能装rest[t1]的水了
y-=rest[t1];
rest[t1]=0;
merge(t1,t1+1);
}
else rest[t1]-=y,y=0;
}
}
}
return 0 ;
}

总结:

   1WA了啊,因为没加ne[x]<=n这个条件,仔细想想,为什么要加这个条件。


再附一个网络版的AC代码:(是有点记忆化的思想在里面的)

//wlb 记忆化的 也算是并查集吧
#include<bits/stdc++.h>
using namespace std;
int num[210000],pre[210000],now[210000];
int pour(int x,int y) {
if(x==-1) return x;
if(now[x]==num[x]) return pre[x]=pour(pre[x],y);
else if(now[x]<num[x]) {
now[x]+=y;
if(now[x]>num[x]) {
int t=now[x]-num[x];
now[x]=num[x];
return pre[x]=pour(pre[x],t);
}
else return x;
}
}
int main() {
int n,a,b,m;
cin>>n;
for(int i=1; i<=n; i++) {
scanf("%d",&num[i]);
pre[i]=i+1;
now[i]=0;
}
pre[n]=-1;
scanf("%d",&m);
while(m--) {
int op;
scanf("%d",&op);
if(op==1) {
scanf("%d%d",&a,&b);
pour(a,b);
}
else {
scanf("%d",&a);
printf("%d\n",now[a]);
}
}
return 0 ;
}

终于找到了上面那些方法TLE的真正原因!

其实,改成这样就好了。

#include<bits/stdc++.h>

using namespace std;
const int MAX = 2e5 + 5;
int a[MAX],rest[MAX],f[MAX];
int getf(int v) {
return f[v] == v ? v : f[v] = getf(f[v]);
}
void merge(int u,int v) {//默认u < v
int t1 = getf(u);
int t2 = getf(v);
if(t1 != t2) f[t1] = t2;
}
int main()
{
int n,m,x,y,op;
cin>>n;
for(int i = 1; i<=n; i++) scanf("%d",a+i),rest[i]=a[i],f[i]=i;
// rest[n+1] = INT_MAX;
f[n+1] = n+1;
cin>>m;
while(m--) {
scanf("%d",&op);
if(op == 2) {
scanf("%d",&x);
printf("%d\n",a[x] - rest[x]);
}
else {
scanf("%d%d",&x,&y);
int t1=getf(x);
while(t1 <= n && y>0) {
if(y>=rest[t1]) {//只能装rest[t1]的水了
y-=rest[t1];
rest[t1]=0;
merge(t1,t1+1);
t1 = getf(t1);
}
else rest[t1]-=y,y=0;
}
}
}
return 0 ;
}

聪明的你一定看得出来,其实就是把t1 = getf(t1); 移到if里面了,想一下,确实是啊!我操作完了之后需要准备出下一个状态并交由while去判断,而不是while完了之后再找下一个状态!你如果while完了再转移到下一个状态,那肯定要加if(...&& t1<=n)啊!因为这里就需要再判断一遍while里面的东西!!而因为一个t1=getf(t1),其中y没有参与,所以if里面不需要加上while里面的(y>0),所以就有了第一个AC代码的样子、、、但是其实你要知道这是不对的!

至于为什么不在if中加(t1<=n)而是在rest[n+1]=INT_MAX这样是TLE的,我是这样认为,还没有验证正确性:

     因为你的想法是弄一个无穷大来表示可以盛放无穷多的水,但是INT_MAX在这里不是无穷大了!这不是图论啊!每一次倒水的范围是1e9!所以倒两次1e9,你这个rest[n+1]无穷大就减没了,甚至rest[n+1]变成负数了,,就进入无限递归了   ,因为if(y>=rest[n+1])这个永远成立,进入if后,y变大了?!然后rest[t1]=0,然后下一层还是可以进。。。(不不不,可能就退出while了)

   emmm并且如果你没有for(i=0 -> MAX) f[i]=i的话,那到t1=n的时候,merge(n,n+1)了之后因为f[n+1]=n+1了所以没事,然后到while这里,注意这时候t1还是n!所以还是可以进入while,然后merge(n+1,n+2),,这时候凉凉了,所有以n为根节点的节点全都变为n+2的祖先节点,而f[n+2]=0,(因为没有初始化!)所以凉凉,在这之后的操作,我要找倒水的位置了,结果跑回0节点这个非法节点了,然后因为rest[0]=0,所以肯定还会进这个if,然后merge(0,1),,这就真凉了,绕圈了。