「HEOI2015」公约数数列 题解

分块。

首先查看询问。我们要求最小的 \(p\),使得 \(\gcd(a_0,a_1,\dots,a_p) \times \operatorname{xor}(a_0,a_1,\dots,a_p)=x\)(其中 \(\operatorname{xor}\) 为异或和)。下面我们从 \(1\) 开始标号。

首先能够得到 \(\gcd\) 的一些性质。

  1. \(\gcd(\gcd(a_1,a_2,\dots,a_n),\gcd(b_1,b_2,\dots,b_m))=\gcd(a_1,a_2,\dots,a_n,b_1,b_2,\dots,b_m)\),所以两个块之间的 \(\gcd\) 可以合并;
  2. \(\gcd(a_1,a_2,\dots,a_p) \geq \gcd(a_1,a_2,\dots,a_{p+1})\),即新加入一个数的 \(\gcd\) 一定小于等于之前的 \(\gcd\)
  3. 对于前缀 \(\gcd\),每次发生改变,至少变到原来的 \(\dfrac{1}{2}\),即取值最多有 \(O(\log a_i)\) 个。

好了有了这两个性质我们就有想法了。维护下面四个东西:

  • 块内 \(\operatorname{xor}\) 和;
  • 块内 \(\gcd\)
  • 对于每个位置,求出这个位置作为结尾的前缀异或和;
  • 每一块头尾两个数的前缀 \(\gcd\)。(这个可以直接通过第二个东西求出来)。

对于修改,我们直接修改掉原来的元素,然后因为这个修改不会影响其他块,所以对于这个块我们暴力重构,时间复杂度 \(O(\sqrt n)\)

对于查询,如果说这一块头尾两个数的前缀 \(\gcd\) 是相等的,说明这块内没有发生变化,所以可以直接在排好序的块内二分查找查询是否有解就行了。

否则,暴力查询,遍历块内是否有解。因为不同的 \(\gcd\) 值最多有 \(O(\log a_i)\) 个,所以时间复杂度 \(O(\sqrt n \log ^2 n)\)

总时间复杂度 \(O(q\sqrt n \log ^2n)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
struct block{
	LL val,pos;
	block(){val=pos=0;}
	block(LL V,LL P){val=V,pos=P;}
	bool operator < (block another) const {return (val<another.val) || (val==another.val && pos<another.pos);}
}s[100005];
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
LL n,a[100005],q,p,l[400],r[400],pos[100005],g[100005],x[100005];
void replace(LL t)
{
	g[l[t]]=x[l[t]]=a[l[t]];
	s[l[t]]=block(a[l[t]],l[t]);
	for(LL i=l[t]+1;i<=r[t];++i)
	{
		g[i]=gcd(g[i-1],a[i]);
		x[i]=x[i-1]^a[i];
		s[i]=block(x[i],i);
	}
	sort(s+l[t],s+r[t]+1);
}
LL binarysearch(LL l,LL r,LL val)
{
	LL ans=l;
	while(l<=r)
	{
		LL mid=(l+r)>>1;
		if(s[mid].val>=val)	ans=mid,r=mid-1;
		else	l=mid+1;
	}
	return ans;
}
LL query(LL u)
{
	LL ans=114514;
	LL gd=a[1],xo=0;
	for(LL i=1;i<=pos[n] && ans==114514;++i)
	{
		if(gcd(gd,g[r[i]])==gd)
		{
			if(u%gd==0)
			{
				LL p=(u/gd)^xo;
				LL pos=binarysearch(l[i],r[i],p);
				if(s[pos].val==p)	ans=s[pos].pos;
			}
			gd=gcd(gd,g[r[i]]);
			xo^=x[r[i]];
		}
		else
		{
			for(LL j=l[i];j<=r[i];++j)
			{
				gd=gcd(gd,a[j]);
				xo^=a[j];
				if(gd*xo==u)
				{
					ans=j;
					break;
				}
			}
		}
	}
	return ans;
}
int main(){
	scanf("%lld",&n);
	p=sqrt(n);
	for(LL i=1;i<=n;++i)
	{
		scanf("%lld",&a[i]);
		pos[i]=(i-1)/p+1;
		if(!l[pos[i]])	l[pos[i]]=i;
		r[pos[i]]=i;
	}
	for(LL i=1;i<=pos[n];++i)	replace(i);
	LL q;
	scanf("%lld",&q);
	while(q-->0)
	{
		char o[8];
		scanf("%s",o);
		if(o[0]=='M')
		{
			LL where,val;
			scanf("%lld %lld",&where,&val);
			++where;
			a[where]=val;
			replace(pos[where]);
		}
		else
		{
			LL val;
			scanf("%lld",&val);
			LL output=query(val);
			if(output==114514)	puts("no");
			else	printf("%lld\n",output-1);
		}
	}
	return 0;
}
posted @ 2020-07-30 20:42  SyadouHayami  阅读(122)  评论(0编辑  收藏  举报

My Castle Town.