• $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);
    	}
    }
    
    
  • 相关阅读:
    Python3爬虫系列:理论+实验+爬取妹子图实战
    虚机安装后无网卡、网卡驱动
    Linux运维工程师面试题整理
    睡眠或者重启windows,无法ssh连接或者pingVMware的虚机
    W10: Warning: Changing a readonly file使用vi/vim报错问题解决
    keyboard-interactive authentication with the ssh2 server failed 的SecureCRT报错解决
    公网访问内网实现(内网穿透)
    Linux内网时钟同步问题(ntp和chrony)
    xshell的快捷复制粘贴设置
    Linux中shell去除空行的几种方法
  • 原文地址:https://www.cnblogs.com/wo-shi-zhen-de-cai/p/11238353.html
Copyright © 2020-2023  润新知