这道题非常适合作为st表的模板题。
题目传送门:https://www.luogu.com.cn/problem/P14221
本题采用贪心策略。
首先考虑暴力做法:枚举每个修改的L和R,查出最大的值。提前用数组记录前缀和后缀GCD,并用st表记录修改后的值并查询区间GCD,答案即为
时间复杂度
接着思考:需要我们枚举每个L和R吗?发现:时,此时L取i一定劣于取i+1,因为最终答案中,
一项相等,但是
一项在换成i时单调不减,同理于R。因此,我们只需要枚举所有L使得
即所有的R使得
即可。由于gcd的取值仅有log k个,因此时间复杂度为
最后注意一个小细节:可以不用修改,故记得给ans初始赋值为f1(n)
代码实现
#include<bits/stdc++.h>
using namespace std;
const long long N=3e5+10;
long long a[N],d[N],f1[N],f2[N],n,k,T;
long long dp[N][20];
inline long long GCD(long long a,long long b){
if(a<b)swap(a,b);
if(!b)return a;
return GCD(b,a%b);
}
inline long long query(long long L,long long R){
long long tmp=floor(log2(R-L+1));
return GCD(dp[L][tmp],dp[R+1-(1<<tmp)][tmp]);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>T;
while(T--){
vector<long long>l;
vector<long long>r;
cin>>n>>k;f2[n+1]=0;
for(long long i=1;i<=n;i++)cin>>a[i];
for(long long i=1;i<=n;i++){
dp[i][0]=a[i]+k;
f1[i]=GCD(f1[i-1],a[i]);
}
for(long long i=n;i>=1;i--){
f2[i]=GCD(f2[i+1],a[i]);
}
for(long long i=1;i<=n;i++){
if(f1[i]!=f1[i-1])l.push_back(i);
}
for(long long i=n;i>=1;i--){
if(f2[i]!=f2[i+1])r.push_back(i);
}
for(long long j=1;(1<<j)<=n;j++){
for(long long i=1;i+(1<<j)<=n+1;i++){
dp[i][j]=GCD(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
}
long long ans=f1[n];
for(long long i=0;i<l.size();i++){
for(long long j=0;r[j]>=l[i]&&j<r.size();j++){
long long tmp=GCD(f1[l[i]-1],f2[r[j]+1]);
tmp=GCD(tmp,query(l[i],r[j]));
//cout<<l[i]<<' '<<r[j]<<' '<<tmp<<endl;
ans=max(ans,tmp);
}
}
cout<<ans<<endl;
}
return 0;
}
274

被折叠的 条评论
为什么被折叠?



