CF1548B题解

在日报上面看到的,发现 NOIP 模拟赛考过这个 trick(

首先我们把题目要求的条件这么写:

\[a_i=x_i \times m+k \]

那么我们要找到满足条件的数组,差分后的数组一定都是 \(m\) 的倍数,换句话说差分后的 \(\gcd\) 一定大于 \(1\)

这里已经可以用线段树+st 表大力草了,但是显然我们并不满足于此。

我们考虑通过双指针,钦定右端点,然后移动左端点。

然后你发现不可能删掉左区间的元素,所以我们考虑用两个栈来维护这个区间。

大概就是给定了一个中点 \(mid\),然后对于 \(i \in [l,mid]\) 维护一个 \(\gcd_{k=i}^{mid}a_k\),对于 \(i \in (mid,r]\) 维护一个 \(\gcd_{k=mid+1}^i a_k\)

于是移动左端点的时候就只需要直接 check 就好了。

\(l > mid\) 时,令 \(mid=r\),然后重构整个左边的栈,把右边的栈清空。

可以知道每一个元素只会被加入一次和被重构一次,所以复杂度很明显是 \(O(n\log V)\) 的。

#include<cstdio>
typedef unsigned ui;
typedef unsigned long long ull;
const ui M=2e5+5;
ui T,n;ull a[M],f[M];
inline ull gcd(const ull&a,const ull&b){
	return b?gcd(b,a%b):a;
}
signed main(){
	ui i,j,l,mid,ans;scanf("%u",&T);
	while(T--){
		scanf("%u",&n);l=mid=1;ans=0;
		for(i=1;i<=n;++i)scanf("%llu",a+i),a[i-1]=a[i-1]>a[i]?a[i-1]-a[i]:a[i]-a[i-1];
		for(i=1;i<n;++i){
			f[i]=i==1||i-1==mid?a[i]:gcd(f[i-1],a[i]);
			while(l<=mid&&gcd(f[l],f[i])==1)++l;
			if(l>mid){
				f[i]=a[mid=i];
				for(j=i-1;j>=l;--j)f[j]=gcd(a[j],f[j+1]);
				while(l<=i&&f[l]==1)++l;
			}
			if(i-l+1>ans)ans=i-l+1;
		}
		printf("%u\n",++ans);
	}
}
posted @ 2022-01-11 15:08  Prean  阅读(28)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};