BZOJ4028: [HEOI2015]公约数数列
Description
设计一个数据结构. 给定一个正整数数列 a_0, a_1, ..., a_{n - 1},你需要支持以下两种操作:
Input
输入数据的第一行包含一个正整数 n.
Output
对于每个 QUERY 询问,在单独的一行中输出结果。如果不存在这样的 p,输出 no.
Sample Input
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
Sample Output
0
no
2
8
8
HINT
对于 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.
题解Here!
本来想用线段树维护一下,然后发现我好像需要$3$个$\log_2n$,这是要爆炸的节奏啊。。。
所以我们拿出了分块。
一开始我还在想,$GCD$和$XOR$有什么联系,然后发现,这不需要联系啊。。。
首先,$GCD$有个还算不错的性质:
对于一列数组,从左往右取前缀$gcd$,不同的值最多只有$\log_2n$种。
并且每次值如果改变,那么前缀$gcd$的值至少除以二。
对于每个块,维护下列信息:
块内数据$xor$和,块内$gcd$,块的头尾两个数的前缀$gcd$,块内每个数以块左端点为头的前缀$xor$和。
对于第四类信息,还需要用某种方法,使得支持在$\log_2n$的时间内询问是否存在一个数。
修改的时候,修改位置所在块暴力重构,后面的块更新第三类信息即可。
查询的时候,如果某个块的第三类信息相等,说明这个块内前缀$gcd$都不变,有没有解查表就知道了。
这个表怎么搞呢?
假设暴力扫描,如果前面的块所取到的前缀$gcd$为$lastgcd$,$xor$为$lastxor$。
若$gcd(lastgcd,Gcd[r[i]])==lastgcd$,则说明这个块内所有的数取$gcd$后都是$lastgcd$,那么$xor[j]=(\frac{x}{lastgcd}\quad xor \quad lastxor)$。
然后在另一个排好序的数组中二分查找就可以了。
否则,这个块内暴力访问看一下是否有解,因为不同的$gcd$值不超过$\log_2n$种,所以暴力访问次数并不多。
所以复杂度就是$O(n\sqrt n\log_2n)$。
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define MAXN 100010
using namespace std;
int n,m,block;
int colour[MAXN],Left[MAXN],Right[MAXN];
long long val[MAXN],gcd_sum[MAXN],xor_sum[MAXN];
struct node{
long long x;
int id;
friend bool operator <(const node &p,const node &q){
if(p.x==q.x)return p.id<q.id;
return p.x<q.x;
}
}a[MAXN];
inline long long read(){
long long date=0,w=1;char c=0;
while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
return date*w;
}
long long gcd(long long x,long long y){
if(!y)return x;
return gcd(y,x%y);
}
int half_find(int l,int r,long long x){
int mid,ans=l;
while(l<=r){
mid=l+r>>1;
if(a[mid].x>=x){ans=mid;r=mid-1;}
else l=mid+1;
}
return ans;
}
void build(int x){
gcd_sum[Left[x]]=xor_sum[Left[x]]=val[Left[x]];
a[Left[x]]=(node){val[Left[x]],Left[x]};
for(int i=Left[x]+1;i<=Right[x];i++){
gcd_sum[i]=gcd(gcd_sum[i-1],val[i]);
xor_sum[i]=xor_sum[i-1]^val[i];
a[i]=(node){xor_sum[i],i};
}
sort(a+Left[x],a+Right[x]+1);
}
int solve(long long x){
int ans=-1;
long long Gcd=val[1],Xor=0;
for(int i=1;i<=colour[n]&&ans==-1;i++){
if(gcd(Gcd,gcd_sum[Right[i]])==Gcd){
if(x%Gcd==0){
long long k=(x/Gcd)^Xor;
int pos=half_find(Left[i],Right[i],k);
if(a[pos].x==k){
ans=a[pos].id;
break;
}
}
Gcd=gcd(Gcd,gcd_sum[Right[i]]);Xor^=xor_sum[Right[i]];
}
else{
for(int j=Left[i];j<=Right[i];j++){
Gcd=gcd(Gcd,val[j]);Xor^=val[j];
if(Gcd*Xor==x){
ans=j;
break;
}
}
if(ans!=-1)break;
}
}
return ans;
}
void work(){
char ch[10];
long long x,y;
while(m--){
scanf("%s",ch);x=read();
if(ch[0]=='M'){
x++;y=read();
val[x]=y;
build(colour[x]);
}
else{
int s=solve(x);
if(s==-1)printf("no\n");
else printf("%d\n",s-1);
}
}
}
void init(){
n=read();
block=(int)sqrt(n);
for(int i=1;i<=n;i++){
colour[i]=(i-1)/block+1;
if(!Left[colour[i]])Left[colour[i]]=i;
Right[colour[i]]=i;
val[i]=read();
}
for(int i=1;i<=colour[n];i++)build(i);
m=read();
}
int main(){
init();
work();
return 0;
}

浙公网安备 33010602011771号