P5278 算术天才⑨与等差数列

传送门

题解:

题目中要求查询一段区间内的数能否构成公差为K的等差数列。由于等差数列本身不好维护,那么我们就可以去找区间内的数能构成等差数列的等价条件,再维护这些条件即可。

通过看别人的题解,我们可以得到这些条件:

1.这些数不相同。(废话)

2.最大值减去最小值等于(l-r)k。(直接套公式,还是挺显然的)

3.所有相邻两数的差的gcd都为k,即都为k的倍数。(????????)

等差数列肯定满足以上条件,所以我们只需要来伪证一下它的充分性即可。

首先,所有相邻两数的差都是k的倍数,那么我们可以知道任意两数的差都是k的倍数,因为 任意两数的差 都可以用几个 相邻两数的差 通过加减得到。

所以,所有数与最小值的差都是k的倍数。这段区间最大值与最小值的差是(l-r)k,所以每个数与最小值做差得到的值不会超过(l-r)k那么这些差就只有0,k,2k,3k…(l-r)k这(l-r+1)种情况。又因为这段区间有(l-r+1)个且互不相同的数,所以只有可能是把拿(l-r+1)种情况都占满了的,也就是等差数列。

在伪证完了之后,我们来考虑如何维护这些性质。鉴于是区间查询,所以考虑线段树维护。最大值和最小值不用说,gcd只需要记录一个数和它下一个数的差,再与其它差求gcd,查询只需要查询(l,r-1)即可。至于如何确定这些数不同,我们需要用一个pre数组记录每个位置的 上一个与这个位置有相同值的位置的编号,查询时只需要查询最大的pre,只要它小于l就行。我们可以先将值离散,再把拥有相同值的位置的编号丢将进set里让它自动排序,就可以维护pre数组了。

代码如下(自带常数大性质的我开了o2才水过,还不如去维护区间平方和和立方和碰碰运气)

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 3e5+5;
int readint()
{
	int x=0,f=1;char s=getchar();
	#define sc (s=getchar())
	while(s<'0'||s>'9'){if(s=='-')f=-1;sc;}
	while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+(s^48);sc;}
	return x*f;
}
int gcd(int a,int b)
{
	return b?gcd(b, a%b):a;
}
struct zz
{
	int l,r;
	int s;
	int goal_l,goal_r;
	int maxx,minn;
}tr[maxn<<2];
int a[maxn];
int fabs(int x)
{
	if(x<0)
		x=-x;
	return x;
}
void push_up(int num)
{
	tr[num].goal_l=tr[num<<1].goal_l;
	tr[num].goal_r=tr[num<<1|1].goal_r;
	int xx=fabs(tr[num<<1].goal_r-tr[num<<1|1].goal_l);
	tr[num].s=gcd(gcd(tr[num<<1].s,tr[num<<1|1].s),xx);
	tr[num].maxx=max(tr[num<<1].maxx,tr[num<<1|1].maxx);
	tr[num].minn=min(tr[num<<1].minn,tr[num<<1|1].minn);
}
void build(int num,int l,int r)
{
	tr[num].l=l;
	tr[num].r=r;
	if(l!=r)
	{
		int mid=(l+r)/2;
		build(num<<1,l,mid);
		build(num<<1|1,mid+1,r);
		push_up(num);
	}
	else
	{
		tr[num].goal_l=a[l];
		tr[num].goal_r=a[l];
		tr[num].maxx=a[l];
		tr[num].minn=a[l];
	}
}
void updata(int num,int l,int r,int x)
{
	if(l<=tr[num].l&&tr[num].r<=r)
	{
		tr[num].goal_l=x;
		tr[num].goal_r=x;
		tr[num].maxx=x;
		tr[num].minn=x;
	}
	else
	{
		int mid=(tr[num].l+tr[num].r)>>1;
		if(l<=mid)
			updata(num<<1,l,r,x);
		if(r>mid)
			updata(num<<1|1,l,r,x);
		push_up(num);
	}
}
zz nx;
zz ask(int num,int l,int r)
{
	if(l<=tr[num].l&&tr[num].r<=r)
		return tr[num];
	else
	{
		int mid=(tr[num].l+tr[num].r)>>1;
		zz ans=nx;
		zz ans1,ans2;
		bool f1=0,f2=0;
		if(l<=mid)
		{
			f1=1;
			ans1=ask(num*2,l,r);
		}
		if(r>mid)
		{
			f2=1;
			ans2=ask(num*2+1,l,r);
		}
		if(f1)
			if(f2)
			{
				int xx=fabs(ans1.goal_r-ans2.goal_l);
				ans.s=gcd(gcd(ans1.s,ans2.s),xx);
				ans.maxx=max(ans1.maxx,ans2.maxx);
				ans.minn=min(ans1.minn,ans2.minn);
				ans.goal_l=ans1.goal_l;
				ans.goal_r=ans2.goal_r;
			}
			else
				ans=ans1;
		else
			ans=ans2;
		return ans;
	}
}
int main()
{
	//freopen("card.in","r",stdin);
	//freopen("card.out","w",stdout);
	int n,m;
	n=readint();
	m=readint();
	for(int i=1;i<=n;i++)
		a[i]=readint();
	build(1,1,n);
	int l,r,k;
	int sx=0;
	for(int i=1;i<=m;i++)
	{
		int c=readint();
		if(c==1)
		{
			l=readint(),r=readint();
			l=l^sx;
			r=r^sx;
			updata(1,l,l,r);
		}
		else
		{
			l=readint(),r=readint(),k=readint();
			l=l^sx;
			r=r^sx;
			k=k^sx;
			zz t=ask(1,l,r);
			if(t.s==k&&(t.maxx-t.minn)==k*(r-l)||l==r)
			{
				printf("Yes\n");
				sx++;
			}
			else
				printf("No\n");
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值