1. 程式人生 > 實用技巧 >「HEOI2015」公約數數列 題解

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