BZOJ4028: [HEOI2015]公约数数列

BZOJ4028: [HEOI2015]公约数数列

Description

设计一个数据结构. 给定一个正整数数列 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表示最大公约数。

Input

 输入数据的第一行包含一个正整数 n.

接下来一行包含 n 个正整数 a_0, a_1, ..., a_{n - 1}.
之后一行包含一个正整数 q,表示询问的个数。
之后 q 行,每行包含一个询问。格式如题目中所述。

Output

对于每个 QUERY 询问,在单独的一行中输出结果。如果不存在这样的 p,输出 no.

Sample Input

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

Sample Output

6
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;
}

 

posted @ 2018-08-26 20:31  符拉迪沃斯托克  阅读(334)  评论(1编辑  收藏  举报
Live2D