题目传送门:(Luogu[JSOI2009])等差数列
真是一道神仙的题目(QAQ)……
这一题中共有两个操作,一个是区间增加等差数列,一个是询问。
先来看第一个,常见的操作,差分(+)线段树,两个单点一个区间即可。
第二个操作,先原数列这样处理一下:(a[i]=a[i+1]-a[i])(注意是后减前,放在前一个的位置上)
这样我们就可以直接在原数列上差分了,而等差数列有个极为显然的性质:公差相等。
所以我们的询问就变成了:查找一段区间中有多少段相同的数(?)
我保证肯定有人想维护区间有多少段相等的数,但是重点是对于一个等差数列来说,
他的开头不一定和后面的数相等,所以我们要分类讨论,讨论一个区间的左右两个端点是否被算入。
(S0:)区间不包含左右端点的答案。(S1:)区间仅包含左端点的答案。
(S2:)区间仅包含右端点的答案。(S3:)区间同时包含左右端点的答案。如图:
所以在合并时分类讨论就可以了,举个例子:(ans.S0=min(min(Lef.S0+Rig.S1,Lef.S2+Rig.S0),Lef.S2+Rig.S1-(Lef.r==Rig.l));)
(大家自己看一下吧,懒得解释的说。)答案为(S3)。
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int f=1,w=0;char x=0;
while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
return w*f;
}
const int N=100010;
int n,Q,a[N];
struct Segment
{
int lef,rig;
int l,r,S0,S1,S2,S3,add;
}tree[N<<2];
inline void Add(Segment &ans,Segment Lef,Segment Rig)
{
ans.lef=Lef.lef,ans.rig=Rig.rig,ans.l=Lef.l,ans.r=Rig.r;
ans.S0=min(min(Lef.S0+Rig.S1,Lef.S2+Rig.S0),Lef.S2+Rig.S1-(Lef.r==Rig.l));
ans.S1=min(min(Lef.S1+Rig.S1,Lef.S3+Rig.S0),Lef.S3+Rig.S1-(Lef.r==Rig.l));
ans.S2=min(min(Lef.S2+Rig.S2,Lef.S0+Rig.S3),Lef.S2+Rig.S3-(Lef.r==Rig.l));
ans.S3=min(min(Lef.S1+Rig.S3,Lef.S3+Rig.S2),Lef.S3+Rig.S3-(Lef.r==Rig.l));
}
inline void Spread(int pos)
{
if(tree[pos].add)
{
tree[pos<<1].l+=tree[pos].add,tree[pos<<1|1].l+=tree[pos].add;
tree[pos<<1].r+=tree[pos].add,tree[pos<<1|1].r+=tree[pos].add;
tree[pos<<1].add+=tree[pos].add,tree[pos<<1|1].add+=tree[pos].add;
tree[pos].add=0;
}
}
inline void Build(int pos,int l,int r)
{
if(l==r)
{
tree[pos].add=0;
tree[pos].l=tree[pos].r=a[l];
tree[pos].S0=0;tree[pos].S1=1;
tree[pos].lef=tree[pos].rig=l;
tree[pos].S2=1;tree[pos].S3=1;
return ;
}
int mid=(l+r)>>1;tree[pos].add=0;
Build(pos<<1,l,mid);Build(pos<<1|1,mid+1,r);
Add(tree[pos],tree[pos<<1],tree[pos<<1|1]);
}
inline void Change(int pos,int l,int r,int d)
{
if(l<=tree[pos].lef&&tree[pos].rig<=r)
{
tree[pos].l+=d,tree[pos].r+=d;
tree[pos].add+=d;
return ;
}
Spread(pos);
int mid=(tree[pos].lef+tree[pos].rig)>>1;
if(l<=mid) Change(pos<<1,l,r,d);
if(r>mid) Change(pos<<1|1,l,r,d);
Add(tree[pos],tree[pos<<1],tree[pos<<1|1]);
}
inline Segment Ask(int pos,int l,int r)
{
if(l<=tree[pos].lef&&tree[pos].rig<=r) return tree[pos];
Spread(pos);Segment ans;
int mid=(tree[pos].lef+tree[pos].rig)>>1;
if(r<=mid) return Ask(pos<<1,l,r);
if(l>mid) return Ask(pos<<1|1,l,r);
Add(ans,Ask(pos<<1,l,mid),Ask(pos<<1|1,mid+1,r));
return ans;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("3.in","r",stdin);
freopen("myself.out","w",stdout);
#endif
n=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<n;i++) a[i]=a[i+1]-a[i];
Build(1,1,n-1);Q=read();
while(Q--)
{
char C[20];scanf("%s",C);
int s=read(),t=read(),a,b;
if(C[0]=='A')
{
a=read(),b=read();
if(s!=1) Change(1,s-1,s-1,a);
if(t!=n) Change(1,t,t,-a-b*(t-s));
if(s!=t) Change(1,s,t-1,b);//注意这里的细节问题,因为我们的处理,所以要判断。
}
else printf("%d
",s==t?1:Ask(1,s,t-1).S3);
}
}