题解:CF2227E It All Went Sideways
前言
本来不会的来着,看到其他题解单调栈三个字突然醒悟。
前置知识
单调栈
题目解析
我们定义横着数称为行,竖着数称为列。
容易注意到方块移动当且仅当
每次弹出计算代价时,设
下面是证明和细节:
- 不会漏算:显然这些方块从来没移动,因为
A_i 是区间({l_i},i] 最小的,在区间中高度为[A_i,A_k] 之间这些格子没动过。 - 不会多算:单调栈计算完这些高度后会立刻弹出,不会参与后续计算
- 等于的情况:因为我们的
l_i 不考虑等于情况,每次计算区间({l_i},i] 会包含等于的值,为了不重复计算,我们在等于时也弹出。 -
对于移除一个放格如何令答案更大,考虑在单调栈最终剩下的所有数字,移除格子的下标
别忘记答案减去
复杂度是线性的,因为只用到了单调栈。
正解代码
跑的飞快。
#include<bits/stdc++.h>
#define int long long
using namespace std;
signed main()
{
int t;
cin>>t;
while(t--)
{
int n,a[200005]={0},ans=0;
int l[200005]={0};
stack<int> stk;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=n;i>=1;i--)
{
while(!stk.empty() && a[stk.top()]>a[i])
{
l[stk.top()]=i;
stk.pop();
}
stk.push(i);
}
while(!stk.empty())
{
l[stk.top()]=0;
stk.pop();
}
//for(int i=1;i<=n;i++) cout<<l[i]<<" ";
int res=0,num=INT_MAX;
for(int i=1;i<=n;i++)
{
while(!stk.empty() && a[stk.top()]>=a[i])
{
int b=stk.top();
ans+=(b-l[b])*(a[b]-a[i]);
stk.pop();
}
stk.push(i);
}
while(!stk.empty())
{
int b=stk.top();
res=max(res,b-l[b]);
num=min(num,b);
stk.pop();
}
cout<<ans+res-1<<"\n";
}
return 0;
}