可惜,考场上差点就把这道*3400切掉了。
考虑倒着做,每次等价于将一个满足 a i = max j ≤ i a j a_i=\max_{j\le i} a_j ai=maxj≤iaj或者 a i = max j ≥ i a j a_i=\max_{j\ge i} a_j ai=maxj≥iaj的位置上的 a i a_i ai变成一个大于 a i a_i ai的数 x x x
发现 x x x是单调不降的,因此从小到大考虑每一个数
考虑对于每个位置维护 { l i } , { r i } \{l_i\},\{r_i\} {li},{ri}分别表示这个数成为前缀最大值或者后缀最大值的最小代价,那么对于当前扫描到的权值,记录其出现的最左边的位置为 L L L,最右边的位置为 R R R,那么每次操作等价于:
1.1 1.1 1.1 将 [ L , n ] [L,n] [L,n]中的 l i l_i li加上 1 1 1
1.2 1.2 1.2 将 [ 1 , R ] [1,R] [1,R]中的 r i r_i ri加上 1 1 1
1.3 1.3 1.3 将 [ 1 , R ] [1,R] [1,R]中的 l i l_i li变成 min ( l i , r i ) \min(l_i,r_i) min(li,ri)
1.4 1.4 1.4 将 [ L , n ] [L,n] [L,n]中的 r i r_i ri变成 min ( l i , r i ) \min(l_i,r_i) min(li,ri)
线段树+矩阵乘法即可。
复杂度 O ( n log n ) O(n\log n) O(nlogn)。
remark \text{remark} remark 感觉自己还是不够冷静,考试的时候有点着急。其实这种模拟赛的成绩大可不必看得那么重。还是应该相信自己的实力的。
#include
#define ll long long
#define pb push_back
#define fi first
#define se second
#define inf 0x3f3f3f3f
using namespace std;
const int N=2e5+5;
int n,a[N];
vector<int>nums[N];
int sz;
ll res;
struct Data{
int a11,a12,a21,a22,a33;
Data(int _a11=0,int _a12=inf,int _a21=inf,int _a22=0,int _a33=0){a11=_a11,a12=_a12,a21=_a21,a22=_a22,a33=_a33;}
Data operator +(const Data &r)const{
Data res;
res.a11=min(a11+r.a11,a12+r.a21);
res.a12=min(a11+r.a12,a12+r.a22);
res.a21=min(a21+r.a11,a22+r.a21);
res.a22=min(a21+r.a12,a22+r.a22);
res.a33=a33+r.a33;
return res;
}
};
Data t[N<<2];
void pushdown(int p){
t[p<<1]=t[p<<1]+t[p],t[p<<1|1]=t[p<<1|1]+t[p];
t[p].a11=t[p].a22=t[p].a33=0,t[p].a12=t[p].a21=inf;
}
void update(int p,int l,int r,int ql,int qr,Data x){
if(ql<=l&&r<=qr){
t[p]=t[p]+x;
return;
}
int mid=l+r>>1;pushdown(p);
if(ql<=mid)update(p<<1,l,mid,ql,qr,x);
if(mid<qr)update(p<<1|1,mid+1,r,ql,qr,x);
}
int query(int p,int l,int r,int x){
if(l==r)return min({t[p].a11,t[p].a12,t[p].a21,t[p].a22});
int mid=l+r>>1;pushdown(p);
return x<=mid?query(p<<1,l,mid,x):query(p<<1|1,mid+1,r,x);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;for(int i=1;i<=n;i++)cin>>a[i],nums[a[i]].pb(i);
for(int i=1;i<=n;i++){
sz=nums[i].size();if(sz==0)continue;
for(int j=0;j<sz;j++)res+=query(1,1,n,nums[i][j])+1;
update(1,1,n,nums[i][0],n,{1,inf,inf,0,0});
update(1,1,n,1,nums[i].back(),{0,inf,inf,1,0});
update(1,1,n,1,nums[i].back(),{0,inf,0,0,0});
update(1,1,n,nums[i][0],n,{0,0,inf,0,0});
}cout<<res;
}