给定一个数列,要求资磁以下两种操作:
1.单点修改.
2.求数列中最前的位置p,使前缀最大公约数gcd*前缀异或和xor==一个输入的数x.
考虑分块+暴力.
按照
单点修改时对所在块暴力更新gcd与xor
每次更新的复杂度是
查询时分两种情况
①如果GCD(gcd_now,gcd_pre)==gcd_pre,即到当前块尾gcd没有改变.
则当前块前缀gcd唯一,所以符合的前缀xor值也是唯一的。
在这一块中二分查找是否存在即可.
②如果gcd不相等,那么对这一块从头到尾暴力扫描答案.
但是由于gcd最多改变
总复杂度不超过
实现起来跟讲的一样复杂。。想的时候没想到查询①…
上代码
#include<bits/stdc++.h>
#define LL long long
#define clr(x,i) memset(x,i,sizeof(x))
using namespace std;
const int N=100005;
inline void read(LL &x)
{
x=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
}
struct data{
LL xt,id;
friend bool operator <(data a,data b){
return a.xt==b.xt ? a.id<b.id : a.xt<b.xt;
}
}b[N];
LL n,q,a[N],gs[N],xs[N];
int id[N],l[666],r[666],tim,tot;
LL GCD(LL a,LL b)
{
if(!b)return a;
return GCD(b,a%b);
}
void work(int x)
{
int be=l[x],en=r[x];
gs[be]=xs[be]=b[be].xt=a[be];
b[be].id=be;
for(int i=be+1;i<=en;i++){
gs[i]=GCD(gs[i-1],a[i]);
xs[i]=xs[i-1]^a[i];
b[i].xt=xs[i];b[i].id=i;
}
sort(b+be,b+en+1);
}
int find(LL x,int l,int r)
{
int mid,ans=l;
while(l<=r){
mid=(l+r)>>1;
if(b[mid].xt>=x)
ans=mid,r=mid-1;
else
l=mid+1;
}
return ans;
}
int main()
{
read(n);
tim=sqrt((double)n);tot=(int)(n-1)/tim+1;
//printf("%d %d\n",tim,tot);
for(int i=1;i<=n;i++)read(a[i]);
for(int i=1;i<=n;i++){
id[i]=(i-1)/tim+1;
if(!l[id[i]])l[id[i]]=i;
r[id[i]]=i;
}
for(int i=1;i<=tot;i++)
work(i);
read(q);
while(q--)
{
char op[10];LL x,y;
scanf("%s",op);
if(op[0]=='M'){
read(x);read(y);x++;
a[x]=y;
work(id[x]);
}
else{
read(x);
LL gl=a[1],xl=0,flag=0;
for(int i=1;i<=tot&&!flag;i++)
{
int be=l[i],en=r[i];
if(GCD(gs[en],gl)==gl)
{
if(x%gl==0){
LL xans=(x/gl)^xl;
int pos=find(xans,be,en);
if(b[pos].xt==xans){
printf("%d\n",b[pos].id-1);flag=1;break;
}
//
}
xl^=xs[en];//gl=GCD(gl,gs[en]);
}
else{
for(int j=be;j<=en;j++)
{
gl=GCD(gl,a[j]);xl^=a[j];
if(gl*xl==x){
printf("%d\n",j-1);flag=1;break;
}
}
if(flag)break;
}
}
if(!flag)printf("no\n");
}
}
return 0;
}
之前公式挂了,重发一遍