LGP3246 [HNTS 2016] 序列 学习笔记
LGP3246 [HNTS 2016] 序列 学习笔记
前言
用 \(\texttt{LGP4755}\) 复健然后写下来令人忍俊不禁。所以我超级生气暴怒。再写一题。
题意简述
给定序列 \(A\)。\(m\) 次询问,每次问你 \([l,r]\) 的所有子区间(包括它自己)的最小值之和。
\(1\le n,q\le 10^5\),\(|a_i|\le 10^9\)。
做法解析(一)
首先这的确而显然地可以用莫队干掉。思考一下区间扩张(收缩)的代价即可。比如说右端点右移 \(1\) 的代价?
我们设 \(f_{l,r}\) 为从 \([l,r-1]\) 拓展到 \([l,r]\) 时产生的额外贡献,则有 \(f_{l,r}=f_{l,pre[r]}+a_r\times(r-pre[r])\),其中 \(pre[r]\) 表示 \(r\) 左边第一个小于 \(a_r\) 的元素的下标。发现 \(f\) 的转移与 \(l\) 无关,可以把 \(l\) 这维去掉,然后递推就行了。
但是如果 \(pre[r]<l\) 这个转移显然就错了。怎么办呢?我们直接找到 \([l,r]\) 范围内能取到 \(a_x\) 最小值的下标 \(p\),实际上的转移贡献就是 \(a_p\times(l-p+1)+\sum f_i[i\in (p,r]]\)。正确性显然,因为由定义,恰从 \(f_{p+1}\) 开始,其 \(pre\) 肯定就在 \([l,r]\) 范围内了。
其它的按莫队流程走就完了。不赘述。
代码实现(一)
//鸽了。别急。
做法解析(二)
再来看看只需要树状数组的扫描线做法吧。
首先还是建笛卡尔树,毕竟是“静态对于所有区间问涉及区间最值”。然后显然问题可以变成二维数点(左右端点的范围各一条轴)。询问和修改都是二维矩形。使用区间加区间和树状数组即可。
记录我们沿着扫的维为 \(x\),维护的维为 \(y\)。所以我们的树状数组应该维护每个 \(y=i\) 上的历史和。对于询问我们差分成两段前缀和的差即可。至于矩形给历史和的贡献还是拆成一次函数。
注意,虽然你平时差分矩形询问时总是写 Query(l-1,-1)+Query(r,1),这是对的,但你不能在写矩形加时也这么写。因为矩形加不是“从一块挖掉一部分”,而是“在 l 处出现贡献,然后在 r+1 处把它抵掉”(或者你可以认为一个关键的区别是,拆成一次函数时它不会自己在哪个 x 处停下来,而询问相当于就是在 x 截断,所以询问那么写而修改这么写)。
代码实现(二)
#include <bits/stdc++.h>
using namespace std;
namespace obasic{
typedef long long lolo;
const int M107=1e9+7;
template <typename _T>
void readi(_T &x){
_T k=1;x=0;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')k=-1;
for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-'0';
x*=k;return;
}
void readis(){};
template <typename _T, typename... _Ts>
void readis(_T &x,_Ts &...xs){readi(x),readis(xs...);}
template <typename _T>
void writi(_T x){
if(x<0)putchar('-'),x=-x;
if(x>9)writi(x/10);
putchar(x%10+'0');
}
template <typename _T>
void writil(_T x){writi(x),puts("");}
template <typename _T>
void writip(_T x){writi(x),putchar(' ');}
};
using namespace obasic;
const int MaxN=1e5+5;
int N,M,A[MaxN],X,Y;
int stk[MaxN],ktp,ls[MaxN],rs[MaxN];
struct anob{lolo l,r,w,id;};
vector<anob> S[MaxN],Q[MaxN];
void dfs(int u,int cl,int cr){
if(!u)return;
S[cl].push_back({u,cr,A[u],0});
S[u+1].push_back({u,cr,-A[u],0});
dfs(ls[u],cl,u-1),dfs(rs[u],u+1,cr);
}
struct BinidTree{
lolo ta[MaxN],ts[MaxN],n;
void init(int x){n=x;fill(ta,ta+n+1,0),fill(ts,ts+n+1,0);}
int lowbit(int x){return x&(-x);}
void add(int p,lolo x){for(int q=p;q&&q<=n;q+=lowbit(q))ta[q]+=x,ts[q]+=p*x;}
void adds(int l,int r,lolo x){add(l,x),add(r+1,-x);}
lolo gts(int p){lolo res=0;for(int q=p;q;res+=(p+1)*ta[q]-ts[q],q-=lowbit(q));return res;}
lolo getsum(int l,int r){return gts(r)-gts(l-1);}
}kT,bT;
lolo ans[MaxN];
int main(){
readis(N,M);kT.init(N),bT.init(N);
for(int i=1;i<=N;i++)readi(A[i]);
for(int i=1,k;i<=N;i++){
k=ktp;while(k&&A[stk[k]]>=A[i])ls[i]=stk[k--];
rs[stk[k]]=i,stk[++k]=i,ktp=k;
}
dfs(stk[1],1,N);
for(int i=1;i<=M;i++){
readis(X,Y);
Q[X-1].push_back({X,Y,-1,i});
Q[Y].push_back({X,Y,1,i});
}
for(int i=1;i<=N;i++){
for(auto [l,r,w,id] : S[i])kT.adds(l,r,w),bT.adds(l,r,-w*(i-1));
for(auto [l,r,w,id] : Q[i])ans[id]+=w*(kT.getsum(l,r)*i+bT.getsum(l,r));
}
for(int i=1;i<=M;i++)writil(ans[i]);
return 0;
}
后记
做完才发现这就是 \(\texttt{LGP8868}\) 的弱化版。
浙公网安备 33010602011771号