一堆人,一个人可以吃相邻的的比他小的人,吃完体积相加。问每个人可以达到的最大体积?
首先不好想的话就想边界,这里的边界显然是最大的那个人,他可以吃掉所有人。然后对于一个一般的人,他首先可以吃掉两侧比他小的人,这可以单调栈求出两侧第一个更大的人,然后把中间的人都吃掉,这可以前缀和。然后再取挑战两侧更大的人,如果此时,比两侧原本更大的人还大了,就可以吃掉两侧更大的人,并且两侧更大的人能吃的,他现在一定也能吃,因此这个人的答案就应该是两侧更大的人的答案。如果还是没有两侧更大的人大,答案就是此时的大小。
那两侧更大的人的答案呢?我们可以根据初始大小排序,从大到小计算答案,这样就可以保证更大的人的答案已经算出来了,然后最开始,最大的那个人前面讨论过了,显然可以直接求出。
然后这里有个问题,如果吃完中间的,比两侧更大的人 l , r l,r l,r都大,应该取谁的答案作为当前答案?答案是都行,因为如果吃完中间的,比两侧更大的人都大,那么 s u m ( l + 1 , r − 1 ) > a l , s u m ( l + 1 , r − 1 ) > a r sum(l+1,r-1)>a_l,sum(l+1,r-1)>a_r sum(l+1,r−1)>al,sum(l+1,r−1)>ar。
那么也就有 a l + s u m ( l + 1 , r − 1 ) > a ( r ) , a r + s u m ( l + 1 , r − 1 ) > a l a_l+sum(l+1,r-1)>a(r),a_r+sum(l+1,r-1)>a_l al+sum(l+1,r−1)>a(r),ar+sum(l+1,r−1)>al。也就是说 l , r l,r l,r在吃完中间这一段之后,都会比对方大, [ l , r ] [l,r] [l,r]这一段的人都可以合体,然后继续去吃外面的,从 l , r l,r l,r出发都可以到达这个合体的状态,所以 l , r l,r l,r的答案必然一样,所以取哪个都行。
如果中间的和之比一边的更大值大,就取这一边的。
void solve(){
cin>>n;
vi a(n+1);
rep(i,1,n)cin>>a[i];
vector<int>l(n+1,0),r(n+1,n+1);
stack<int>s;
for(int i=1;i<=n;i++){
while(s.size()&&a[i]>=a[s.top()]){
s.pop();
}
if(s.size())l[i]=s.top();
s.push(i);
}
s=stack<int>();
for(int i=n;i>=1;i--){
while(s.size()&&a[i]>=a[s.top()]){
s.pop();
}
if(s.size())r[i]=s.top();
s.push(i);
}
vvi b;
rep(i,1,n){
b.push_back({a[i],i});
}
sort(b.begin(),b.end(),[](vi &x,vi &y){
return x[0]>y[0];
});
vi sum(n+1),ans(n+1);
rep(i,1,n)sum[i]=sum[i-1]+a[i];
rep(i,0,n-1){
int j=b[i][1];
if(j==1){
if(a[j+1]>=a[j]){
ans[j]=a[j];
continue;
}
}
else if(j==n){
if(a[j-1]>=a[j]){
ans[j]=a[j];
continue;
}
}
else{
if(a[j-1]>=a[j]&&a[j+1]>=a[j]){
ans[j]=a[j];
continue;
}
}
int s=sum[r[j]-1]-sum[l[j]];
if(l[j]>=1&&s>a[l[j]]){
ans[j]=ans[l[j]];
}
else if(r[j]<=n&&s>a[r[j]]){
ans[j]=ans[r[j]];
}
else{
ans[j]=s;
}
}
rep(i,1,n)cout<<ans[i]<<' ';
}