Integers Have Friends cf 1549D 【ST表+单调队列】

CodeForces 1549D -> Click Here

题意

数组\(a_1,a_2,a_3,...,a_n\)​​中定义一个 subarray \(a_i,a_{i+1},a_{i+2},...,a_{j}\)​​​ 为friends group 当且仅当存在一个\(m\)​使得\(a_i \%m=a_{i+1}\%m=...=a_j\%m\)​ (\(x\%y\)​​​​表示x除y的余数)​,询问长度最大的 friends group

思路

因为对\(m\)​​​取余数所以若存在一个\(m\)​​使\(a\%m=b\%m=c\%m\)​​则\(a\)​​,\(b\)​​,\(c\)​​之间差值的最大公约数\(\geq2\)​​

所以建立一个数组\(\bf{B}\),存放\(a\)中相邻数的差值使\(b_i=a_{i+1}-a_i\)

用ST表存B中\(l\)\(r\)​的最大公约数,再用单调队列遍历一遍即可

具体细节见code ↓

code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll A[200005],n;//A存输入的a数组
ll B[200005];//B存A中相邻数的差
ll f[200005][22];//ST表
void ST_prework(){// ST表预处理B中的gcd(最大公约数)
	int t=log(n)/log(2)+1;
	for(int i=1;i<=n;i++) f[i][0]=B[i];
	for(int j=1;j<t;j++)
		for(int i=1;i<=n-(1<<j)+1;i++)
			f[i][j]=__gcd(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
ll query(ll l,ll r){//ST表查找B中l到r的gcd
	int k=log(r-l+1)/log(2);
	return __gcd(f[l][k],f[r-(1<<k)+1][k]);
}
void solve(){
	int ans=0;
	scanf("%lld",&n);
	for(int i=1;i<=n;i++) scanf("%lld",&A[i]);
	for(int i=1;i<=n;i++) B[i]=abs(A[i+1]-A[i]);//做差
	n--;//差数组的大小为n-1
	ST_prework();//预处理
	for(int i=1,j=2;i<=n;i++){//起始位置l为i=1,r为j=2,开始单调队列
		if(i==j) j++;//右端点恒大于左端点
		while((query(i,j)>=2&&j<=n)) j++;//如果l到r的gcd大于二则 右端点向右移动
		if(j-i>=1&&query(i,j-1)>=2)	ans=max(j-i,ans);//若l到r不符合gcd>=2则更新答案
	}
	cout<<ans+1<<endl;//ans存的是差值个数 输出时应ans+1输出
}
int main(){
	int T;scanf("%d",&T);
	while(T--) solve();
	return 0;
}

小节

最开始就观察到了做差取 gcd ,但后面直接\(O(n)\)扫一遍看当前差值是否于前一位的差值的gcd相同

最后样例

1
8
1 3 5 11 17 20 23 3 2

成功将我卡飞

posted @ 2021-08-03 09:56  莳曳  阅读(116)  评论(0)    收藏  举报