HEOI2015 公约数数列

版权声明:随意转载,愿意的话提一句作者就好了 https://blog.csdn.net/stone41123/article/details/84134199

Link

Diffculty

算法难度5,思维难度7,代码难度6

Description

给定长度为 n n 的序列,要求支持两种操作,共 q q 个:

  1. 给定 i d , v id,v ,将 i d id 位置的值修改成 v v
  2. 给定 x x ,求最小正整数 p p 使得 g c d ( a 1 , a 2 , . . . , a p ) × x o r ( a 1 , a 2 , . . . , a p ) = x gcd(a_1,a_2,...,a_p)\times xor(a_1,a_2,...,a_p)=x

1 n 1 0 5 , 1 q 1 0 4 1\le n\le 10^5,1\le q\le 10^4

Solution

神仙题,不会不会。

g c d gcd​ 有个性质,就是前缀 g c d gcd​ 一定是单调不升的,这个很重要,我们可以在这个基础上分块。

我们对序列分块之后,记 f 1 ( i ) f1(i) 为从块头到 i i 的前缀 g c d gcd f 2 ( i ) f2(i) 为从块头到 i i 的前缀 x o r xor

修改就暴力重构这个块。

查询我们一个块一个块处理,我们考虑之前所有块的前缀 g c d gcd 和加上这个块的前缀 g c d gcd 是否相等。

我们记前面所有块的 g c d gcd l a s t g c d lastgcd ,所有块的 x o r xor l a s t x o r lastxor

如果相等,那么我们可以直接在块内找是否存在一个 f 2 ( i ) = x l a s t g c d xor l a s t x o r f2(i)=\frac x {lastgcd} \operatorname{xor}lastxor

这个东西可以预处理和修改的时候每个块都维护一个 m a p map ,就能快速查询了。

扫描二维码关注公众号,回复: 4094437 查看本文章

如果不相等,那么暴力枚举块内每个位置,直接判断。

这样子复杂度就是 O ( q n l o g n ) O(q\sqrt n logn) 了,因为每次变化 g c d gcd 至少减少一半,所以不相等的最多 l o g n logn 次。

代码注意一下细节就好了。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<map>
#define LL long long
using namespace std;
inline int read(){
	int x=0,f=1;char ch=' ';
	while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9')x=x*10+(ch^48),ch=getchar();
	return f==1?x:-x;
}
inline LL readLL(){
	LL x=0;int f=1;char ch=' ';
	while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9')x=x*10LL+(ch^48),ch=getchar();
	return f==1?x:-x;
}
const int N=1e5+5,sqN=320;
map<LL,int> mp[sqN];
int n,a[N],q,block,cnt,belong[N],L[N],R[N],f1[N],f2[N];
inline LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int main(){
	n=read();
	for(int i=1;i<=n;++i)a[i]=read();
	block=sqrt(n);
	cnt=n/block;
	if(n%block)cnt++;
	for(int i=1;i<=n;++i)belong[i]=(i-1)/block+1;
	for(int i=1;i<=cnt;++i)L[i]=(i-1)*block+1,R[i]=i*block;
	R[cnt]=n;
	for(int i=1;i<=cnt;++i){
		f1[L[i]]=a[L[i]];
		f2[L[i]]=a[L[i]];
		mp[i][f2[L[i]]]=L[i];
		for(int j=L[i]+1;j<=R[i];++j){
			f1[j]=gcd(f1[j-1],a[j]);
			f2[j]=f2[j-1]^a[j];
			if(!mp[i][f2[j]])mp[i][f2[j]]=j;
		}
	}
	q=read();
	for(int s=1;s<=q;++s){
		char ch[10];
		scanf("%s",ch+1);
		if(ch[1]=='M'){
			int x=read()+1,v=read();
			a[x]=v;
			int i=belong[x];
			mp[i].clear();
			f1[L[i]]=a[L[i]];
			f2[L[i]]=a[L[i]];
			mp[i][f2[L[i]]]=L[i];
			for(int j=L[i]+1;j<=R[i];++j){
				f1[j]=gcd(f1[j-1],a[j]);
				f2[j]=f2[j-1]^a[j];
				if(!mp[i][f2[j]])mp[i][f2[j]]=j;
			}
		}
		else{
			LL x=readLL();int flag=0;
			LL lastgcd,lastxor;
			for(int i=1;i<=R[1];++i){
				if((LL)f1[i]*f2[i]==x){
					flag=1;
					printf("%d\n",i-1);
					break;
				}
			}
			if(flag)continue;
			lastgcd=f1[R[1]];
			lastxor=f2[R[1]];
			for(int i=2;i<=cnt;++i){
				int nowgcd=gcd(lastgcd,f1[R[i]]);
				if(nowgcd==lastgcd){
					if(x%nowgcd){
						lastxor^=f2[R[i]];
						continue;
					}
				    int num=mp[i][(LL)(x/nowgcd)^lastxor];
					if(num){
						flag=1;
						printf("%d\n",num-1);
						break;
					}
					lastxor^=f2[R[i]];
				}
				else{
					for(int j=L[i];j<=R[i];++j){
					    lastgcd=gcd(lastgcd,a[j]);
						lastxor^=a[j];
						if(lastgcd*lastxor==x){
							flag=1;
							printf("%d\n",j-1);
							break;
						}
					}
					if(flag)break;
				}
			}
			if(flag)continue;
			else printf("no\n");
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/stone41123/article/details/84134199
今日推荐