Codeforces Round 218(Div.2 Only) D. Vessels
問題
\/
\__/
\____/
こんな感じでn個の器が縦に連なっています。
上から順にa[i]リットルの容積があります。容積以上の水が注がれると下の容器に流れていきます。
この容器に関して、2タイプのクエリをm個投げます。
1 p x : p番目のところにxリットル水を注ぐ。
2 k : k番目の容器に何リットル水がたまっているか表示する。
2番目のクエリをちゃんと出力しなさい。
制約
1≦n,m≦200000
1≦a[i],x≦10^9
1≦k,p≦n
解法
真面目にシミュレートするとO(nm)かかるので、
「今の場所より下で、まだ空き容量がある最初の容器は何番目か」をデータとして持っておく必要があります。
コンテスト中はアホなことに配列をがんばって更新する形でやろうとしていたのですが、setを使うと簡潔に書けます。
setは要素の挿入・削除・lower_boundの各計算がO(logn)でできるので速度としては十分です。
というわけで水を注ぐクエリは
while(x>0)
今の場所に注げるだけ水を注ぐ
if(今の場所が満杯)
今の場所をsetから削除
今の場所=lower_bound(set, 今の場所)
という感じになります。
プログラム
#include<iostream> #include <fstream> #include <stdio.h> #include <stdlib.h> #define _USE_MATH_DEFINES #include <math.h> #include<string> #include<vector> #include<cmath> #include<stack> #include<queue> #include<sstream> #include<algorithm> #include<map> #include<complex> #include<ctime> #include<set> using namespace std; #define li long long int #define rep(i,to) for(li i=0;i<((li)(to));i++) #define repp(i,start,to) for(li i=(li)(start);i<((li)(to));i++) #define pb push_back #define sz(v) ((li)(v).size()) #define bgn(v) ((v).begin()) #define eend(v) ((v).end()) #define allof(v) (v).begin(), (v).end() #define dodp(v,n) memset(v,(li)n,sizeof(v)) #define bit(n) (1ll<<(li)(n)) #define mp(a,b) make_pair(a,b) #define rin rep(i,n) #define EPS 1e-10 #define ETOL 1e-8 #define MOD 100000000 #define F first #define S second #define p2(a,b) cout<<a<<"\t"<<b<<endl #define p3(a,b,c) cout<<a<<"\t"<<b<<"\t"<<c<<endl li a[200200]; li naka[200200]; set<li> live; void add(li x, li p){ while(x>0){ li pos=(*live.lower_bound(p)); li buffer=a[pos]-naka[pos]; if(buffer>x){ naka[pos]+=x; x=0; }else{ naka[pos]=a[pos]; x-=buffer; live.erase(pos); } p=pos+1; } } int main(){ li n,m; cin>>n; rin{cin>>a[i];} rin{ naka[i]=0; live.insert(i); } live.insert(n); a[n]=9000000000000000000; cin>>m; li tmp,p,x,k; rep(i,m){ cin>>tmp; if(tmp==1){ cin>>p>>x; add(x,p-1); } else{ cin>>k; cout<<naka[k-1]<<endl; } } return 0; }