題目大意
給定一個長度爲 的序列,每個位置的數爲 ,現在給出 個操作,每次修改某個位置的值,或者詢問區間 中是否能通過修改之多一個位置的值來使得整個序列的最大公約數嚴格等於 。
數據範圍
題解
考慮一次修改操作 ,令 區間內有 個數不能被 整除,則有
- 當且僅當 時,所有數都能被 整除,此時只要修改其中一個數爲 即可使得所有數的最大公約數嚴格等於 。
- 當且僅當 時,將這個數修改成 即可使所有數的最大公約數嚴格等於 。
- 當且僅當 時,無法在一步之內使得這些數的最大公約數嚴格等於 。
考慮如何求出區間內不能被 整除的數的個數。 我們用一顆線段樹維護區間內所有數的最大公約數,這樣修改時,合併兩個子樹的答案是 的,因而這個過程的時間複雜度爲 ,在查詢的時候,若某個區間的最大公約數能被 整除,則說明該區間內的所有數都能被 整除,不需要計算。在遞歸迭代的過程中,若不能被 整除的數的個數已經大於 ,則已經可以判斷答案,不需要再計算。
考慮這個過程的時間複雜度,可以發現, 至多爲 ,而導致 增加的原因只有到達了線段樹的葉子節點,因而一次查詢的時間複雜度不會超過 。因而程序的總時間複雜度爲
代碼如下
#include<bits/stdc++.h>
using namespace std;
#define maxn 500010
struct node{
node *left,*right;
int gcds,l,r;
node(int tl=0,int tr=0):l(tl),r(tr),left(NULL),right(NULL){
if(tl==tr) {
scanf("%d",&gcds);
return;
} int mid=(tl+tr)>>1;
left=new node(tl,mid);
right=new node(mid+1,tr);
gcds=__gcd(left->gcds,right->gcds);
}
void change(int x,int d){
if(l==r) return (void)(gcds=d);
if(x<=left->r) left->change(x,d);
else right->change(x,d);
gcds=__gcd(left->gcds,right->gcds);
}
void ask(int tl,int tr,int d,int &ans){
if(ans>1) return;
if(l==r) return (void)ans++;
if(tl<=left->r&&left->gcds%d!=0)
left->ask(tl,min(left->r,tr),d,ans);
if(tr>=right->l&&right->gcds%d!=0)
right->ask(max(right->l,tl),tr,d,ans);
}
};
int main(){
int n,q;
scanf("%d",&n);
node t(1,n);
scanf("%d",&q);
for(int i=1,op;i<=q;i++){
scanf("%d",&op);
if(op==1){
int l,r,d,ans=0;
scanf("%d%d%d",&l,&r,&d);
t.ask(l,r,d,ans);
puts(ans<=1?"YES":"NO");
}else{
int x,d;
scanf("%d%d",&x,&d);
t.change(x,d);
}
}
return 0;
}