【差分】
【差分】
板子
一维差分
//差分数组的加数操作
void insert(int l,int r,int c){
b[l]+=c;
b[r+1]-=c;
}
//预处理 构造差分数组
for(int i=1;i<=n;i++) insert(i,i,a[i]);
二维差分
void insert(int x1,int y1,int x2,int y2,int c){
b[x1][y1]+=c;
b[x2+1][y1]-=c;
b[x1][y2+1]-=c;
b[x2+1][y2+1]+=c;
}
//构造差分数组
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
insert(i,j,i,j,a[i][j]);
}
}
//还原原数组 求二维前缀和
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++){
b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
}
}
一般用来处理区间加减同一个数的问题
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;
}
}

浙公网安备 33010602011771号