luogu P4108 [HEOI2015]公约数数列

背景:

剩下的博客有空再补。

题目传送门:

https://www.luogu.org/problem/P4108

题意:

一个序列,两种操作。
[ 1 ] . [1]. [1].将第 x x x位置的值改为 y y y
[ 2 ] . [2]. [2].询问 gcd ⁡ ( a 1 , a 2 , a 3 . . . , a p ) ∗ xor ( a 1 , a 2 , a 3 , . . . , a p ) = x \gcd(a_1,a_2,a_3...,a_p)*\text{xor}(a_1,a_2,a_3,...,a_p)=x gcd(a1,a2,a3...,ap)xor(a1,a2,a3,...,ap)=x的最小的位置 p p p

思路:

之前提到过,前缀 gcd ⁡ \gcd gcd的个数是 log ⁡ 值 域 \log值域 log级别的。
那么考虑分块。
维护当前这一块的前缀 gcd,xor \text{gcd,xor} gcd,xor(当前这一块的前缀),以及用个 map \text{map} map维护前缀 xor \text{xor} xor出现在当前这一块中的位置。
修改直接暴力将块内的信息全都修改掉。
考虑询问,前缀 gcd ⁡ \gcd gcd的个数是 log ⁡ 值 域 \log值域 log级别的,不妨考虑查看当前这一块末尾的前缀 gcd \text{gcd} gcd(整个序列的前缀)是否等于上一块的前缀 gcd \text{gcd} gcd(整个序列的前缀)。
若等于,我们在当前这一块的 map \text{map} map查看序列中是否存在 x G C D \frac{x}{GCD} GCDx GCD \text{GCD} GCD为这个前缀 gcd ⁡ \gcd gcd)存在则在 map \text{map} map找到那个最小的位置。
反之,直接进去暴力寻找,因为前缀 gcd \text{gcd} gcd的个数可以保证。
理论时间复杂度: Θ ( n log ⁡ 2 值 域 ) \Theta(n\log^2值域) Θ(nlog2),实际约为: Θ ( n log ⁡ 值 域 ) \Theta(n\log值域) Θ(nlog)
注意 long long \text{long long} long long的问题。

代码:

实际块的大小为 n 0.55 n^{0.55} n0.55比较快,可以卡过。

#pragma GCC optimize("Ofast")
#include<cstdio>
#include<cstring>
#include<map>
#include<cmath>
#include<algorithm>
#define LL long long
#define R register
#define I inline
using namespace std;
	int n,m,Sqrt,cnt=0;
	int d[100010],belong[100010];
	struct node{int l,r;int h[1510],g[1510];map<int,int> MAP;} a[1510];//h:异或,g:gcd 
int gcd(int x,int y)
{
	return !y?x:gcd(y,x%y);
}
I void work(int x)
{
	int op=0,t=1;
	a[x].MAP.clear();
	a[x].g[1]=a[x].h[1]=d[a[x].l],a[x].MAP[d[a[x].l]]=a[x].l;
	for(R int i=a[x].l+1;i<=a[x].r;i++)
	{
		t++;
		a[x].g[t]=gcd(a[x].g[t-1],d[i]);
		a[x].h[t]=(a[x].h[t-1]^d[i]);
		if(!a[x].MAP[a[x].h[t]]) a[x].MAP[a[x].h[t]]=i;
	}
}
I void init()
{
	//Sqrt=sqrt(n);
	Sqrt=pow(n,0.55);
	int last=-1,now;
	for(R int i=1;i<=n;i++)
	{
		now=i/Sqrt+1;
		belong[i]=now;
		if(now!=last) a[cnt].r=i-1,a[++cnt].l=i;
		last=now;
	}
	a[cnt].r=n;
	for(R int i=1;i<=cnt;i++)
		work(i);
}
I int solve(LL x)
{
	int tmp1=a[1].g[1],tmp2=0;
	if((LL)a[1].g[1]*a[1].h[1]==x) return 1;
	for(R int i=1;i<=cnt;i++)
	{
		int limit=a[i].r-a[i].l+1;
		if(tmp1!=gcd(tmp1,a[i].g[limit]))
		{
			for(R int j=1;j<=limit;j++)
			{
				int yy=gcd(tmp1,a[i].g[j]);
				if(!(x%yy))
				{
					int op=a[i].MAP[(x/yy)^tmp2];
					if(op) return op;
				}
			}
		}
		else if(!(x%tmp1))
		{
			int op=a[i].MAP[(x/tmp1)^tmp2];
			if(op) return op;
		}
		tmp1=gcd(tmp1,a[i].g[limit]),tmp2^=a[i].h[limit];
	}
	return -1;
}
I char getc()
{
	static char buf[1<<20],*fs,*ft;
	return(fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<20,stdin)),fs==ft)?EOF:*fs++;
}
I void readll(LL &x)
{
	char ch=getc(),f=1;
	for(x=0;ch=='\n'||ch==' ';ch=getc());
	for(;ch!='\n'&&ch!=' ';ch=getc())
		x=((x+(x<<2))<<1)+(ch^0x30);
}
I void read(int &x)
{
	char ch=getc(),f=1;
	for(x=0;ch=='\n'||ch==' ';ch=getc());
	for(;ch!='\n'&&ch!=' ';ch=getc())
		x=((x+(x<<2))<<1)+(ch^0x30);
}
I void read_str(char *str)
{
	char ch=getc();
	for(;ch=='\n'||ch==' ';ch=getc());
	for(;ch!='\n'&&ch!=' ';ch=getc())
		*str=ch,str++;
	*str='\0';
}
char s[10];
int main()
{
	int x,y;
	LL z;
	read(n);
	for(R int i=1;i<=n;i++)
		read(d[i]);
	init();
	read(m);
	for(R int i=1;i<=m;i++)
	{
		read_str(s+1);
		if(s[1]=='M')
		{
			read(x),read(y);
			x++;
			d[x]=y;
			work(belong[x]);
		}
		else
		{
			readll(z);
			int tmp=solve(z);
			if(tmp==-1) printf("no\n"); else printf("%d\n",tmp-1);
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值