解决区间可以重复贡献的问题,比如,区间最小值,区间最大值,区间gcd,多次计算不影响结果,但是区间求和就不行
eg:给你一个序列Hi,和m次询问(si,ti),要求你从(si,Hsi)-->(ti,Hti)只能向9个方向走(3*3),并且要求到达i位置纵位置不能<Hi,相当于是跨山峰
发现无论中间经过的山峰有多复杂,我只要满足“最高的”山峰可以跨过去就行,那我用hi代表最高山峰,hi-si+hi-ti就是需要斜着走的步数?不是,因为可能会有山阻挡,所以要分别算,从si到maxhi预先向上走的步数,从maxhi到ti预先向下走的步数,贪心:
向上走,一步也不多走,走maxh-hsi;下也是;平走,是区间距离-斜走,但是斜走的只有上走-预留的(预留的必须走),不算里面
最后你发现算预留就是(上)max[hi-hsi-(i-si)],所以st表处理出来
int f[N][21],f1[N][21],f2[N][21];
int n,Q,l2[N];
int main()
{
//freopen("qiandao4.in","r",stdin);
// freopen("dfs.txt","w",stdout);
n=re(); l2[1]=0;l2[2]=1;
_f(i,1,n)
{
f[i][0]=re();
f1[i][0]=f[i][0]-i;
f2[i][0]=f[i][0]+i;
// chu("f")
}
_f(i,3,n)l2[i]=l2[i>>1]+1;
Q=re();
_f(i,1,20)
{
_f(j,1,n)
{
if(j+(1<<i)-1 >n)break;
f[j][i]=max(f[j][i-1],f[j+(1<<(i-1))][i-1]);
f1[j][i]=max(f1[j][i-1],f1[j+(1<<(i-1))][i-1]);
f2[j][i]=max(f2[j][i-1],f2[j+(1<<(i-1))][i-1]);
}
}
_f(i,1,Q)
{
int si=re(),ti=re();int len=l2[ti-si+1];ll ans=0;
if(si<ti)
{
int len=l2[ti-si+1];
int max_h=max(f[si][len],f[ti-(1<<len)+1][len]);
int max_s=max(f1[si][len],f1[ti-(1<<len)+1][len])-f1[si][0];
int max_t_=max(f2[si][len],f2[ti-(1<<len)+1][len])-f2[ti][0];
// chu("\n%d+%d:%d %d+%d:%d\n",si,len,f2[si][len],ti-(1<<len)+1,len,f2[ti-(1<<len)+1][len]);
int cnt=ti-si-(max_h-f[si][0]-max_s)-(max_h-f[ti][0]-max_t_);
// chu("预留上升的:%d,预留下降的:%d,自己最高的:%d,平走:%d\n",max_s,max_t_,max_h,cnt);
ans=(ll)(max_h-f[si][0])*4+(ll)(max_h-f[ti][0])+(ll)cnt*2;
}
else
{
swap(si,ti);
int len=l2[ti-si+1];
int max_h=max(f[si][len],f[ti-(1<<len)+1][len]);
int max_s=max(f1[si][len],f1[ti-(1<<len)+1][len])-f1[si][0];
int max_t_=max(f2[si][len],f2[ti-(1<<len)+1][len])-f2[ti][0];
int cnt=ti-si-(max_h-f[si][0]-max_s)-(max_h-f[ti][0]-max_t_);
ans=(ll)(max_h-f[si][0])+(ll)(max_h-f[ti][0])*4+(ll)cnt*2;
}
chu("%lld\n",ans);
}
return 0;
}
给你一个序列,多次询问,求给定区间每个子区间的最小值的和值
多个询问[L,R]考虑离线分块莫队,对于每次在整个区间多出来的1个数,考虑多出来的贡献,每个数,假如当前区间是L,R,向右拓展一个位置pos[R+1],那么这个数的产生在L,R的贡献就是分为2个部分(1)第一个部分就是找到L,R+1区间的min,这部分的贡献就是val[min](pos_min-L+1),对应的是左端点,右端点就是R+1(2)第二个就是pos_min+1--R+1的区间的贡献,不一定来自val[R+1],比如2 3 0 4 3 6,最小值都不在6出现,但是可以预处理sum_R[i],单个表示i位置在前面最多产生的贡献,就是val[i](i-L[i]+1),sum_R[i]=\(\sum_{i=1}^{i}\)val[i]*(i-L[i]+1) sum_R[R+1]-sum_R[pos]就是pos+1~R+1的贡献,为什么不直接sum[R+1]-sum[L-1]?因为R+1不一定能延伸到L的位置,所以会算上多余的不属于R+1的贡献,min相当于找到了那个连续的继承(前缀和是从上一个L【i】-1转移)点,所以就转移过来了
int n,Q;
int a[maxn];
int q[maxn];
int pre[maxn],nxt[maxn];
ll f[maxn],g[maxn];
int pos[maxn][22];
int get(int x,int y)
{
if(a[x]<a[y]) return x;
return y;
}
int lg[maxn];
struct P{
int l,r,id,bl;
}b[maxn];
bool cmp(P a,P b)
{
if(a.bl==b.bl) return a.r<b.r;
return a.bl<b.bl;
}
ll rig(int r,int l)
{
int len=r-l+1;
int po=get(pos[l][lg[len]],pos[r-(1<<(lg[len]))+1][lg[len]]);
ll tmp=0;
tmp=(ll)(po-l+1)*a[po];
tmp+=f[r]-f[po];
return tmp;
}
ll lef(int l,int r)
{
int len=(r-l+1);
int po=get(pos[l][lg[len]],pos[r-(1<<(lg[len]))+1][lg[len]]);
ll tmp=0;
tmp=(ll)(r-po+1)*a[po];
tmp+=g[l]-g[po];
return tmp;
}
ll ans[maxn];
bool cmpx(P a,P b)
{
return a.id<b.id;
}
int main()
{
n=read();Q=read();
for(int i=1;i<=n;i++) a[i]=read();
a[n+1]=-inf;
int l=1,r=0;
for(int i=1;i<=n;i++)
{
while(l<=r&&a[q[r]]>=a[i])
{
nxt[q[r]]=i;
r--;
}
pre[i]=q[r];
q[++r]=i;
}
while(l<=r&&a[q[r]]>=a[n+1])
{
nxt[q[r]]=n+1;r--;
}
for(int i=1;i<=n;i++) f[i]=f[pre[i]]+(ll)(i-pre[i])*(ll)a[i];
for(int i=n;i>=1;i--) g[i]=g[nxt[i]]+(ll)(nxt[i]-i)*(ll)a[i];
for(int i=1;i<=n;i++) pos[i][0]=i;
for(int j=1;j<=20;j++)
for(int i=1;i<=n;i++) pos[i][j]=get(pos[i][j-1],pos[i+(1<<(j-1))][j-1]);
lg[1]=0,lg[2]=1;
for(int i=3;i<=n;i++) lg[i]=lg[i>>1]+1;
int block=sqrt(n);
for(int i=1;i<=Q;i++)
{
b[i].l=read();b[i].r=read();b[i].id=i;
b[i].bl=(b[i].l-1)/block+1;
}
sort(b+1,b+Q+1,cmp);
int L=1,R=0;
ll now=0;
for(int i=1;i<=Q;i++)
{
while(R<b[i].r)
{
R++;
now+=rig(R,L);
}
while(R>b[i].r)
{
now-=rig(R,L);
R--;
}
while(L<b[i].l)
{
now-=lef(L,R);
L++;
}
while(L>b[i].l)
{
L--;
now+=lef(L,R);
}
ans[b[i].id]=now;
}
for(int i=1;i<=Q;i++) printf("%lld\n",ans[i]);
return 0;
}