「HEOI2015」公约数数列 题解
分块。
首先查看询问。我们要求最小的 \(p\),使得 \(\gcd(a_0,a_1,\dots,a_p) \times \operatorname{xor}(a_0,a_1,\dots,a_p)=x\)(其中 \(\operatorname{xor}\) 为异或和)。下面我们从 \(1\) 开始标号。
首先能够得到 \(\gcd\) 的一些性质。
- \(\gcd(\gcd(a_1,a_2,\dots,a_n),\gcd(b_1,b_2,\dots,b_m))=\gcd(a_1,a_2,\dots,a_n,b_1,b_2,\dots,b_m)\),所以两个块之间的 \(\gcd\) 可以合并;
- \(\gcd(a_1,a_2,\dots,a_p) \geq \gcd(a_1,a_2,\dots,a_{p+1})\),即新加入一个数的 \(\gcd\) 一定小于等于之前的 \(\gcd\);
- 对于前缀 \(\gcd\),每次发生改变,至少变到原来的 \(\dfrac{1}{2}\),即取值最多有 \(O(\log a_i)\) 个。
好了有了这两个性质我们就有想法了。维护下面四个东西:
- 块内 \(\operatorname{xor}\) 和;
- 块内 \(\gcd\);
- 对于每个位置,求出这个位置作为结尾的前缀异或和;
- 每一块头尾两个数的前缀 \(\gcd\)。(这个可以直接通过第二个东西求出来)。
对于修改,我们直接修改掉原来的元素,然后因为这个修改不会影响其他块,所以对于这个块我们暴力重构,时间复杂度 \(O(\sqrt n)\)。
对于查询,如果说这一块头尾两个数的前缀 \(\gcd\) 是相等的,说明这块内没有发生变化,所以可以直接在排好序的块内二分查找查询是否有解就行了。
否则,暴力查询,遍历块内是否有解。因为不同的 \(\gcd\) 值最多有 \(O(\log a_i)\) 个,所以时间复杂度 \(O(\sqrt n \log ^2 n)\)。
总时间复杂度 \(O(q\sqrt n \log ^2n)\)。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
struct block{
LL val,pos;
block(){val=pos=0;}
block(LL V,LL P){val=V,pos=P;}
bool operator < (block another) const {return (val<another.val) || (val==another.val && pos<another.pos);}
}s[100005];
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
LL n,a[100005],q,p,l[400],r[400],pos[100005],g[100005],x[100005];
void replace(LL t)
{
g[l[t]]=x[l[t]]=a[l[t]];
s[l[t]]=block(a[l[t]],l[t]);
for(LL i=l[t]+1;i<=r[t];++i)
{
g[i]=gcd(g[i-1],a[i]);
x[i]=x[i-1]^a[i];
s[i]=block(x[i],i);
}
sort(s+l[t],s+r[t]+1);
}
LL binarysearch(LL l,LL r,LL val)
{
LL ans=l;
while(l<=r)
{
LL mid=(l+r)>>1;
if(s[mid].val>=val) ans=mid,r=mid-1;
else l=mid+1;
}
return ans;
}
LL query(LL u)
{
LL ans=114514;
LL gd=a[1],xo=0;
for(LL i=1;i<=pos[n] && ans==114514;++i)
{
if(gcd(gd,g[r[i]])==gd)
{
if(u%gd==0)
{
LL p=(u/gd)^xo;
LL pos=binarysearch(l[i],r[i],p);
if(s[pos].val==p) ans=s[pos].pos;
}
gd=gcd(gd,g[r[i]]);
xo^=x[r[i]];
}
else
{
for(LL j=l[i];j<=r[i];++j)
{
gd=gcd(gd,a[j]);
xo^=a[j];
if(gd*xo==u)
{
ans=j;
break;
}
}
}
}
return ans;
}
int main(){
scanf("%lld",&n);
p=sqrt(n);
for(LL i=1;i<=n;++i)
{
scanf("%lld",&a[i]);
pos[i]=(i-1)/p+1;
if(!l[pos[i]]) l[pos[i]]=i;
r[pos[i]]=i;
}
for(LL i=1;i<=pos[n];++i) replace(i);
LL q;
scanf("%lld",&q);
while(q-->0)
{
char o[8];
scanf("%s",o);
if(o[0]=='M')
{
LL where,val;
scanf("%lld %lld",&where,&val);
++where;
a[where]=val;
replace(pos[where]);
}
else
{
LL val;
scanf("%lld",&val);
LL output=query(val);
if(output==114514) puts("no");
else printf("%lld\n",output-1);
}
}
return 0;
}