【差分】
【差分】
一般用来处理区间加减同一个数的问题
Sums of Sliding Window Maximum
https://atcoder.jp/contests/abc407/tasks/abc407_f
有关ax+b的斜率差分:两次差分+两次前缀和->优化到O(n)
int n;
bool cmp(PII x,PII y){
if(x.first!=y.first) return x.first<y.first;
return x.second<y.second;
}
/*
(1)求单点满足条件的连续区间:set+从大到小遍历找左右端点 O(nlogn)
(2)处理ax+b的线性分段函数的各点值:两次差分+两次前缀和(斜率、值)O(n)
*/
void solve(){
cin>>n;
//长度为k的子数组的贡献
vector<ll> ans(n+1,0);
vector<PII> a;
for(int i=0;i<n;i++){
ll num;
cin>>num;
a.push_back({num,(ll)i});
}
sort(a.begin(),a.end(),cmp);
array<vector<ll>,3> pre;//两层差分+两层前缀和
pre[0].resize(n+5,0);//注意2+xmin+xmax+1:数组长度会大于n+2
pre[1].resize(n+5,0);
pre[2].resize(n+5,0);
set<ll> pos={-1,n};
/*
for(int i=1;i<=n;i++){
cout<<pre[0][i]<<" "<<pre[1][i]<<" "<<pre[2][i]<<endl;
}
*/
//从最大数往前遍历 找到左右【自己】可以作为最大数的区间
for(int i=n-1;i>=0;i--){
ll val=a[i].first;
ll idx=a[i].second;
//在set里的都是>val的下标
auto ripos=pos.lower_bound(idx);//右边最近的比自己大的数下标
auto lepos=ripos;lepos--;//左边最近的比自己大的数下标
ll r=*ripos-idx-1;
ll l=idx-*lepos-1;
ll xmin=min_(l,r),xmax=max_(l,r);
//cout<<val<<" "<<idx<<" "<<xmin<<" "<<xmax<<endl;
//差分过程:对斜率进行差分
/*注意这边两个下标都要+1:
(1)l:第一个点不累计
(2)r:差分要在r+1上操作
*/
//正斜率
pre[0][1]+=val;
pre[0][1+xmin+1]-=val;
//负斜率
pre[0][1+xmax+1]-=val;
pre[0][2+xmin+xmax+1]+=val;
pos.insert(idx);
}
//第一遍前缀和:复原斜率
for(int i=1;i<=n+2;i++){
pre[1][i]=pre[0][i]+pre[1][i-1];
}
//第二遍前缀和:复原值
for(int i=1;i<=n+2;i++){
pre[2][i]=pre[1][i]+pre[2][i-1];
}
for(int i=1;i<=n;i++){
ans[i]=pre[2][i];
}
for(int i=1;i<=n;i++){
cout<<ans[i]<<endl;
}
}