题解 P4108 【[HEOI2015]公约数数列】
解题思路:
分块。
不同的前缀
设块大小为
考虑修改一个数,我们转化为对一个数异或上另一个数
用set把前缀异或和存起来。单次修改复杂度
考虑查询,我们从左往右扫描每个块,维护当前前缀
若和一个新的块
由于块内暴力还要求
Code:
#include<cstdio>
#include<algorithm>
#include<set>
const int N=100005,siz=318;
#define gcd std::__gcd
#define bel(x)((x-1)/siz+1)
#define mp std::make_pair
typedef long long LL;
int n,m,K,a[N],xp[N];
LL qry;
struct BLOCK{
int val[320],L,R,len,Gcd,Xor[320],tag;
std::set<std::pair<int,int>>s;
void re(){
Gcd=val[1];
for(int i=2;i<=len;++i)Gcd=gcd(Gcd,val[i]);
}
void build(int l,int r){
L=l,R=r,len=r-l+1;
for(int i=l;i<=r;++i)val[i-l+1]=a[i],Xor[i-l+1]=xp[i],s.insert(mp(xp[i],i-l+1));
re();
}
void modify(int pos,int dlt){
pos=pos-L+1;
for(int i=pos;i<=len;++i){
s.erase(mp(Xor[i],i));
s.insert(mp(Xor[i]^=dlt,i));
}
val[pos]^=dlt;
re();
}
void get(int gg,int&ans){
for(int i=1;i<=len;++i){
gg=gcd(gg,val[i]);
if((LL)gg*(Xor[i]^tag)==qry){
ans=L+i-1;
return;
}
}
}
void find(int gg,int&ans){
if(qry%gg!=0)return;
auto it=s.lower_bound(mp((qry/gg)^tag,0));
if(it==s.end()||(it->first^tag)!=(qry/gg))return;
ans=it->second+L-1;
}
}b[320];
int main(){
scanf("%d",&n);
K=bel(n);
for(int i=0;i<n;++i)scanf("%d",a+i),xp[i]=xp[i-1]^a[i];
for(int i=1;i<K;++i)b[i].build((i-1)*siz,i*siz-1);
b[K].build((K-1)*siz,n-1);
for(scanf("%d",&m);m--;){
char opt[12];
scanf("%s",opt);
if(*opt=='M'){
int pos,x;
scanf("%d%d",&pos,&x);
const int dlt=a[pos]^x;a[pos]=x;
for(int i=bel(pos)+1;i<=K;++i)b[i].tag^=dlt;
b[bel(pos)].modify(pos,dlt);
}else{
scanf("%lld",&qry);
int Gcd=0,ans=666666;
for(int i=1;i<=K&&ans==666666;++i){
int lst=Gcd;
Gcd=gcd(Gcd,b[i].Gcd);
if(Gcd!=lst)b[i].get(lst,ans);else
b[i].find(Gcd,ans);
}
if(ans==666666)puts("no");else
printf("%d\n",ans);
}
}
return 0;
}