公约数数列
时间限制: 1 Sec 内存限制: 256 MB
题目描述
设计一个数据结构. 给定一个正整数数列 a_0, a_1, …, a_{n - 1},你需要支持以下两种操作:
1. MODIFY id x: 将 a_{id} 修改为 x.
2. QUERY x: 求最小的整数 p (0 <= p < n),使得 gcd(a_0, a_1, …, a_p) * XOR(a_0, a_1, …, a_p) = x. 其中 XOR(a_0, a_1, …, a_p) 代表 a_0, a_1, …, a_p 的异或和,gcd表示最大公约数。
输入
输入数据的第一行包含一个正整数 n.
接下来一行包含 n 个正整数 a_0, a_1, …, a_{n - 1}.
之后一行包含一个正整数 q,表示询问的个数。
之后 q 行,每行包含一个询问。格式如题目中所述。
输出
对于每个 QUERY 询问,在单独的一行中输出结果。如果不存在这样的 p,输出 no.
样例输入
10
1353600 5821200 10752000 1670400 3729600 6844320 12544000 117600 59400 640
10
MODIFY 7 20321280
QUERY 162343680
QUERY 1832232960000
MODIFY 0 92160
QUERY 1234567
QUERY 3989856000
QUERY 833018560
MODIFY 3 8600
MODIFY 5 5306112
QUERY 148900352
样例输出
6
0
no
2
8
8
【数据规模与约定】
对于 30% 的数据,n <= 10000,q <= 1000.
对于 100% 的数据,n <= 100000,q <= 10000,a_i <= 10^9 (0 <= i < n),QUERY x 中的 x <= 10^18,MODIFY id x 中的 0 <= id < n,1 <= x <= 10^9.
来源
heoi2015 day1
题解
本来想用线段树写,后来发现线段树写不出来。后来脑补一下发现分块可以过,就码了分块。
这里需要用到一个结论,在一段数中gcd的最多只有logn种。
所以对于每个·块,如果gcd相同,就二分答案。
又知道,sumi^tagi(标记)*gcd(tmp,gcdi)=x,把这个式子划一下,求出sumi的值,用二分,哈希,map之类的办法查找一下区间内是否有这个人数字即可。
对于gcd不同的区间,暴力处理,因为最多只有logn个这样的区间,所以总复杂度为n* sqrt(n) *logn。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#define N 100010
#define M 350
#define ll long long
using namespace std;
int n,q,num,s[N],sum[N],blo[N],l[M],r[M],g[M],tag[M];
struct node{
int num,pos;
bool operator<(const node &x)
const{return num<x.num||(num==x.num&&pos<x.pos);}
}t[N];
int gcd(int a,int b)
{
if(!b)return a;
return gcd(b,a%b);
}
int main()
{
int x,y;ll a;char str[10];
scanf("%d",&n);num=sqrt(n);
for(int i=1;i<=n;i++)scanf("%d",&s[i]);
for(int i=1;i<=num;i++)
{
l[i]=r[i-1]+1;r[i]=min(l[i]+num,n);
for(int j=l[i];j<=r[i];j++)
{
blo[j]=i;sum[j]=sum[j-1]^s[j];
g[i]=gcd(g[i],s[j]);t[j]=(node){sum[j],j};
}
sort(t+l[i],t+r[i]+1);
}
scanf("%d",&q);
while(q--)
{
scanf(" %s",str);
if(str[0]=='M')
{
scanf("%d%d",&x,&y);x++;
int pos=blo[x];y^=s[x];s[x]^=y;g[pos]=0;
for(int i=l[pos];i<=r[pos];i++)g[pos]=gcd(g[pos],s[i]);
for(int i=pos+1;i<=num;i++)tag[i]^=y;
for(int i=l[pos];i<x;i++)t[i]=(node){sum[i],i};
for(int i=x;i<=r[pos];i++)sum[i]^=y,t[i]=(node){sum[i],i};
sort(t+l[pos],t+r[pos]+1);
}
else
{
int ans=0;
scanf("%lld",&a);
for(int i=1,tmp=0;i<=num&&!ans;i++)
{
if(gcd(tmp,s[l[i]])==gcd(tmp,g[i]))
{
int tot=(a/gcd(tmp,g[i]))^tag[i];
int tp=lower_bound(t+l[i],t+r[i]+1,(node){tot,0})-t;
tmp=gcd(tmp,g[i]);
if(t[tp].num!=tot)continue;
ans=t[tp].pos;
}
else
{
if(tag[i])
{
for(int j=l[i];j<=r[i];j++)
sum[j]^=tag[i],t[j]=(node){sum[j],j};
tag[i]=0;sort(t+l[i],t+r[i]+1);
}
for(int j=l[i];j<=r[i];j++)
{
if((ll)sum[j]*gcd(tmp,s[j])==a){ans=j;break;}
tmp=gcd(tmp,s[j]);
}
}
}
if(!ans)printf("no\n");
else printf("%d\n",ans-1);
}
}
return 0;
}