好难想呀,看的题解。
http://www.cnblogs.com/maijing/p/4765730.html
很明显gcd是非严格递减的,那么我们处理出Gcd[i]表示从i所在块的开头到i的gcd,Xor[i]表示从i所在块开头到i的xor
假设暴力扫描,如果前面的块所取到的前缀gcd为lastgcd,xor为lastxor
若gcd(lastgcd,Gcd[r[i]])==lastgcd,则说明这个块内所有的数取gcd后都是lastgcd,那么
xor[j]=(x/lastgcd)^lastxor
然后在另一个排好序的数组中二分查找就可以了。
若不相等,那么暴力去扫描,因为每次取gcd都会至少缩小两倍,所以最多O(log n)次,所以复杂度是保证的。(这一步十分巧妙)
修改操作,暴力修改所在块就可以了。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#define maxn 100010
using namespace std;
struct yts
{
long long x;
int id;
}b[maxn];
long long a[maxn];
int L[1010],R[1010],bel[maxn];
long long Gcd[maxn],Xor[maxn];
int n,m,block,tot,T;
char s[10];
bool cmp(yts x,yts y)
{
return x.x<y.x || (x.x==y.x && x.id<y.id);
}
long long gcd(long long x,long long y)
{
if (y==0) return x;
else return gcd(y,x%y);
}
int find(int L,int R,long long x)
{
int l=L,r=R,ans=L;
while (l<=r)
{
int mid=(l+r)/2;
if (b[mid].x>=x) ans=mid,r=mid-1;
else l=mid+1;
}
return ans;
}
void rebuild(int x)
{
Gcd[L[x]]=Xor[L[x]]=a[L[x]];b[L[x]].x=a[L[x]];b[L[x]].id=L[x];
for (int i=L[x]+1;i<=R[x];i++)
Gcd[i]=gcd(Gcd[i-1],a[i]),Xor[i]=Xor[i-1]^a[i],b[i].x=Xor[i],b[i].id=i;
sort(b+L[x],b+R[x]+1,cmp);
}
int main()
{
scanf("%d",&n);
block=sqrt(n);tot=(n-1)/block+1;
for (int i=1;i<=n;i++)
{
bel[i]=(i-1)/block+1;
if (!L[bel[i]]) L[bel[i]]=i;
R[bel[i]]=i;
}
for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
for (int i=1;i<=tot;i++) rebuild(i);
scanf("%d",&T);
while (T--)
{
bool w=0;
int y;
long long x;
scanf("%s",s);
if (s[0]=='M')
{
scanf("%d%lld",&y,&x);
a[y+1]=x;
rebuild(bel[y+1]);
}
else
{
scanf("%lld",&x);
long long GCD=a[L[1]],XOR=0;
for (int j=1;j<=tot && !w;j++)
{
if (gcd(GCD,Gcd[R[j]])==GCD)
{
if (x%GCD==0)
{
long long p=(x/GCD)^XOR;
int pos=find(L[j],R[j],p);
if (b[pos].x==p) {printf("%d\n",b[pos].id-1);w=1;break;}
}
XOR^=Xor[R[j]];GCD=gcd(GCD,Gcd[R[j]]);
}
else
{
for (int k=L[j];k<=R[j];k++)
{
GCD=gcd(GCD,a[k]);XOR=XOR^a[k];
if (GCD*XOR==x) {printf("%d\n",k-1);w=1;break;}
}
if (w) break;
}
}
if (!w) printf("no\n");
}
}
return 0;
}