好难想呀,看的题解。
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; }