$Luogu[JSOI2009]$等差数列

题目传送门:(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);
	}
}

原文地址:https://www.cnblogs.com/wo-shi-zhen-de-cai/p/11238353.html