1 条题解
-
0
用的 @ANIG 的做法,但是 ta 讲得不是很清楚,所以来扩写一下题解。
令 表示区间 的答案,直接区间 DP 是容易的,用决策单调性可以做到 。
但是区间 DP 的下限复杂度显然为 没有前途,考虑拆分贡献。
令 表示使得 的最小 ,当 时令 。
这样 , 为答案值域,上界为 。
为什么上界是这个?考虑极端情况 全部 时,最优策略是分治下去然后每次合并两个分治区间,分治的层数至多为 ,每层答案增加 ,所以上界 。
从小到大枚举 ,当 时有 。
当 时有 ,即在当前段前面找一段最长的 的合并起来。
在线段树里面维护 的值,这样过后 时相当于让 变成 ,当 时有意义,考虑维护哪些 满足 ,可以把 的 扔到
vector记录下来,由于 序列单调,要用的时候可以直接线段树二分找出来是哪段区间。修改 时直接线段树区间覆盖,再看新的 要不要扔到
vector里去, 的地方修改过后因为 从 变为了 ,所以原本 的地方后续都要跟着 一起往前跳, 和 都应该扔进vector里去。模拟实现上述过程,在每个 累加线段树维护出的答案,就能解决本题。
#include<bits/stdc++.h> using namespace std; #define N 1200005 #define int long long #define all(v) v.begin(),v.end() int n,m,ans,a[N];vector<int> dif,v[N]; struct Segment_tree{ int tr[N],mx[N],len[N],tag[N]; void build(int l=0,int r=n+1,int p=1){ len[p]=r-l+1,tag[p]=-1; if(l==r) return tr[p]=mx[p]=l,void(); int mid=(l+r)>>1,ls=p<<1,rs=p<<1|1; build(l,mid,ls),build(mid+1,r,rs); tr[p]=tr[ls]+tr[rs],mx[p]=max(mx[ls],mx[rs]); } void pushdown(int &p,int &ls,int &rs){ if(tag[p]==-1) return; work(ls,tag[p]),work(rs,tag[p]),tag[p]=-1; } int query(int x,int l=0,int r=n+1,int p=1){ if(l==r) return tr[p]; int mid=(l+r)>>1,ls=p<<1,rs=p<<1|1;pushdown(p,ls,rs); return x<=mid?query(x,l,mid,ls):query(x,mid+1,r,rs); } int bound(int x,int l=0,int r=n+1,int p=1){ if(l==r) return l; int mid=(l+r)>>1,ls=p<<1,rs=p<<1|1;pushdown(p,ls,rs); return mx[ls]>=x?bound(x,l,mid,ls):bound(x,mid+1,r,rs); } void cover(int sl,int sr,int x,int l=0,int r=n+1,int p=1){ if(sl<=l&&r<=sr) return work(p,x); int mid=(l+r)>>1,ls=p<<1,rs=p<<1|1;pushdown(p,ls,rs); if(sl<=mid) cover(sl,sr,x,l,mid,ls); if(sr>mid) cover(sl,sr,x,mid+1,r,rs); tr[p]=tr[ls]+tr[rs],mx[p]=max(mx[ls],mx[rs]); } void work(int &p,int &x){tr[p]=x*len[p],mx[p]=tag[p]=x;} }SGT; signed main(){ ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr); cin>>n,SGT.build(); for(int i=1;i<=n;i++) cin>>a[i],m=max(m,a[i]+30); for(int i=1;i<=n;i++) v[a[i]].push_back(i),ans+=i; for(int i=1;i<=m;i++,ans+=SGT.tr[1]){ vector<int> tmp; vector<tuple<int,int,int>> cov; sort(all(dif)),dif.resize(unique(all(dif))-dif.begin()); for(auto it:dif){ int l=SGT.bound(it),r=SGT.bound(it+1)-1,x=SGT.query(it); cov.emplace_back(l,r,x),tmp.push_back(x); } for(auto [l,r,x]:cov) SGT.cover(l,r,x); dif.clear(); for(auto x:tmp) if(SGT.query(x)!=x) dif.push_back(x); for(auto x:v[i]){ if(SGT.query(x)<x) continue; SGT.cover(x,x,x-1),dif.push_back(x); if(SGT.query(x-1)!=x-1) dif.push_back(x-1); } } cout<<ans-(n+1)*m<<"\n"; }
- 1
信息
- ID
- 7648
- 时间
- 2000ms
- 内存
- 512MiB
- 难度
- 9
- 标签
- 递交数
- 21
- 已通过
- 2
- 上传者