题目描述:
题目分析:
(标题取成这样因为实在不知道叫什么好。。)
不难发现我们更新线段树时需要根据每个左端点对应的Max和Min进行加减,而不是像普通的线段树那样区间整体加上一个相同的值。
考虑我们的线段树需要支持什么操作,记每个位置的变化量为
Δ
\Delta
Δ(即当区间整体+1时这个位置需要加的量):
- 给区间 [ l , r ] [l,r] [l,r]每个位置的 Δ \Delta Δ加上一个值 x x x (可正可负)
- 令区间 [ l , r ] [l,r] [l,r]每个位置的答案加上其对应的 Δ ∗ k \Delta*k Δ∗k,(此题中 k = 1 k=1 k=1, [ l , r ] [l,r] [l,r]可以只用 [ 1 , n ] [1,n] [1,n])
- 求区间 [ l , r ] [l,r] [l,r]的答案的和
为此,我们的线段树需要记录这几个量:
- s u m i sum_i sumi :表示该区间答案的和。
- s d i sd_i sdi:表示该区间 Δ \Delta Δ的和。
- k i k_i ki:表示答案整体加 Δ \Delta Δ次数的懒标记。
- t a g i tag_i tagi:表示修改 Δ \Delta Δ的懒标记。
- a d d i add_i addi:(这是一个比较特别的标记,不同的人可能有不同的写法,在后面介绍)
- l e n i len_i leni:该区间的长度,方便pushdown。
p u s h u p pushup pushup就这样写:
inline void upd(int i){
t[i].sum=t[lc].sum+t[rc].sum;
t[i].sd=t[lc].sd+t[rc].sd;
}
问题主要在于 p u s h d o w n pushdown pushdown,我们需要考虑标记优先级的问题,这里假设定义 k k k的优先级高于 t a g tag tag(即先加答案,再修改 Δ \Delta Δ)。
先忽略
a
d
d
add
add标记,考虑标记
(
k
i
,
t
a
g
i
)
(k_i,tag_i)
(ki,tagi)怎么传给
x
x
x,首先肯定有:
s
u
m
x
+
=
k
i
∗
s
d
x
sum_x+=k_i*sd_x
sumx+=ki∗sdx
k
x
+
=
k
i
k_x+=k_i
kx+=ki
t
a
g
x
+
=
t
a
g
i
tag_x+=tag_i
tagx+=tagi
s
d
x
+
=
t
a
g
i
∗
l
e
n
x
sd_x+=tag_i*len_x
sdx+=tagi∗lenx
那么这时候问题就来了!对于
x
x
x的儿子
y
y
y,当
x
x
x
p
u
s
h
d
o
w
n
pushdown
pushdown的时候,它本来应该先答案加上
k
x
∗
s
d
y
k_x*sd_y
kx∗sdy,再修改
s
d
y
sd_y
sdy,然后当
i
i
i下传给
x
x
x再下传给
y
y
y的时候,给答案加上
k
i
∗
s
d
y
k_i*sd_y
ki∗sdy,再修改
s
d
y
sd_y
sdy,可以现在
i
i
i提前传给了
x
x
x,这就导致了传给
y
y
y的时候,
k
i
k_i
ki本来应该是在
s
d
y
sd_y
sdy修改之后再加的,但是由于
k
k
k的优先级高于
t
a
g
tag
tag,所以
s
u
m
y
sum_y
sumy就加少了!少了多少呢?就是少加给
Δ
\Delta
Δ的
k
i
∗
t
a
g
x
k_i*tag_x
ki∗tagx!这就是
a
d
d
add
add所记录的信息。(此处可以稍作思考)
即 a d d i add_i addi记录的是当标记 ( k r t , t a g r t ) (k_{rt},tag_{rt}) (krt,tagrt)下传给 i i i时,将会少加给 i i i的儿子的 s u m sum sum的关于每个位置的 Δ \Delta Δ的信息,即 k r t ∗ t a g i k_{rt}*tag_i krt∗tagi(说的不是特别清楚,结合代码理解一下)
重新梳理一下 p u s h d o w n pushdown pushdown应该怎么写:
inline void pd(int i,int x){
t[x].sum+=t[i].k*t[x].sd+t[i].add*t[x].len;//add记录的是每个位置对答案的额外贡献
t[x].add+=t[i].k*t[x].tag+t[i].add;
t[x].k+=t[i].k;
t[x].tag+=t[i].tag;
t[x].sd+=t[i].tag*t[x].len;
}
另外,对于题目中的 − ( r − l ) -(r-l) −(r−l),其实就是对每个 r r r将 [ L r , r − 1 ] [L_r,r-1] [Lr,r−1]的 Δ − 1 \Delta-1 Δ−1,然后令 [ L r , r − 1 ] [L_r,r-1] [Lr,r−1]的 k + 1 k+1 k+1
Code:
#include<bits/stdc++.h>
#define maxn 300005
#define LL long long
using namespace std;
char cb[1<<20],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<20,stdin),cs==ct)?0:*cs++)
template<class T>inline void read(T &a){
char c;while(!isdigit(c=getc()));
for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
int n,d,m,a[maxn],A[maxn],An,B[maxn],Bn,last[10000005];
LL ans[maxn];
struct node{
int l,r,id; bool operator < (const node &p)const{return r<p.r;}
}Q[maxn];
struct Seg{
int len;
LL sum,sd,tag,k,add;
//sd: sum of delta, tag: whole delta of delta, k: times of delta affected. add: when k+=1, additional tag left for sum.
}t[maxn<<2];
#define lc (i<<1)
#define rc (i<<1|1)
inline void upd(int i){
t[i].sum=t[lc].sum+t[rc].sum;
t[i].sd=t[lc].sd+t[rc].sd;
}
inline void pd(int i,int x){
t[x].sum+=t[i].k*t[x].sd+t[i].add*t[x].len;
t[x].add+=t[i].k*t[x].tag+t[i].add;
t[x].k+=t[i].k;
t[x].tag+=t[i].tag;
t[x].sd+=t[i].tag*t[x].len;
}
inline void down(int i){pd(i,lc),pd(i,rc),t[i].tag=t[i].k=t[i].add=0;}
void build(int i,int l,int r){
t[i].len=r-l+1;
if(l==r) return;
int mid=(l+r)>>1;
build(lc,l,mid),build(rc,mid+1,r);
}
void mdf(int i,int l,int r,int x,int y,LL d){
if(x<=l&&r<=y) {t[i].sd+=d*t[i].len,t[i].tag+=d;return;}
down(i);
int mid=(l+r)>>1;
if(x<=mid) mdf(lc,l,mid,x,y,d);
if(y>mid) mdf(rc,mid+1,r,x,y,d);
upd(i);
}
LL qry(int i,int l,int r,int x,int y){
if(x<=l&&r<=y) return t[i].sum;
down(i);
int mid=(l+r)>>1; LL ret=0;
if(x<=mid) ret+=qry(lc,l,mid,x,y);
if(y>mid) ret+=qry(rc,mid+1,r,x,y);
return ret;
}
void zero(int i,int l,int r,int x){
if(l==r) return void(t[i].sd=0);
down(i);
int mid=(l+r)>>1;
x<=mid?zero(lc,l,mid,x):zero(rc,mid+1,r,x);
upd(i);
}
inline void accumulate(){
t[1].sum+=t[1].sd,t[1].add+=t[1].tag,t[1].k++;
}
int main()
{
freopen("arithmetic.in","r",stdin);
freopen("arithmetic.out","w",stdout);
read(n),read(d),read(m);
if(!n||!m) return 0;
for(int i=1;i<=n;i++) read(a[i]);
for(int i=1;i<=m;i++) read(Q[i].l),read(Q[i].r),Q[i].id=i;
sort(Q+1,Q+1+m);
int L=1; build(1,1,n);
for(int i=1,j=1;i<=n;i++){
int pre=L;
if(a[i]%d==a[i-1]%d) L=max(L,last[a[i]]+1); else L=i;
last[a[i]]=i;
while(pre<L) zero(1,1,n,pre++);//let delta be 0.
while(A[An]>=L&&a[i]>a[A[An]]) mdf(1,1,n,max(A[An-1]+1,L),A[An],-a[A[An]]/d),--An;
mdf(1,1,n,max(A[An]+1,L),i,a[i]/d),A[++An]=i;//max/d
while(B[Bn]>=L&&a[i]<a[B[Bn]]) mdf(1,1,n,max(B[Bn-1]+1,L),B[Bn],a[B[Bn]]/d),--Bn;
mdf(1,1,n,max(B[Bn]+1,L),i,-a[i]/d),B[++Bn]=i;//-min/d
if(L<i) mdf(1,1,n,L,i-1,-1);//-(r-l)
accumulate();
for(;j<=m&&Q[j].r<=i;j++) ans[Q[j].id]=qry(1,1,n,Q[j].l,i);
}
for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
}