CF475D:CGCDSSQ

浅谈\(RMQ\)https://www.cnblogs.com/AKMer/p/10128219.html

题目传送门:https://codeforces.com/problemset/problem/475/D

我们考虑当\(l\)固定之后,\(r\)在某个区间内的\(gcd\)是一样的,而且这样的区间只有\(log\),因为\(gcd\)改变的话至少会除以二。所以我们就可以用\(st\)表存\(gcd\),然后\(log^2\)的去往后跳,统计出一个一个这样的区间,最后离线处理所有的询问即可。

时间复杂度:\(O(nlog^2n)\)

空间复杂度:\(O(nlogn)\)

代码如下:

#include <cstdio>
using namespace std;
typedef long long ll;

const int maxn=1e5+5,pps=1e7+7;

int n,m;
int f[18][maxn];
int a[maxn],Log[maxn];

int read() {
	int x=0,f=1;char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
	for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
	return x*f;
}

struct HASH {
	int tot;
	int head[pps];
	ll sum[maxn*20];
	int nxt[maxn*20],g[maxn*20];
   

	void ins(int v,int cnt) {
		int tmp=v%pps;
		for(int i=head[tmp];i;i=nxt[i])
			if(g[i]==v) {sum[i]+=cnt;return;}
		sum[++tot]=cnt,g[tot]=v,nxt[tot]=head[tmp],head[tmp]=tot;
	}

	ll find(int v) {
		int tmp=v%pps;
		for(int i=head[tmp];i;i=nxt[i])
			if(g[i]==v)return sum[i];
		return 0;
	}
}H;

int gcd(int a,int b) {
	while(b) {
		int tmp=b;
		b=a%b;a=tmp;
	}
	return a;
}

int find(int st,int GCD) {
	for(int i=Log[n-st+1];i;i--)
		if(f[i][st]&&f[i][st]%GCD==0)
			st=st+(1<<i)-1;
	return st+1;
}

void make_ans() {
	Log[0]=-1;
	for(int i=1;i<=n;i++)
		Log[i]=Log[i>>1]+1;
	for(int i=1;i<=17;i++)
		for(int j=1;j+(1<<i)-1<=n;j++)
			f[i][j]=gcd(f[i-1][j],f[i-1][j+(1<<(i-1))]);
	for(int i=1;i<=n;i++) {
		int pos=i,GCD=a[i];
		while(pos<=n) {
			int newpos=find(pos,GCD);
			H.ins(GCD,newpos-pos);
			pos=newpos,GCD=gcd(GCD,a[newpos]);
		}
	}
}

int main() {
	n=read();
	for(int i=1;i<=n;i++)
		f[0][i]=a[i]=read();
	make_ans(),m=read();
	for(int i=1;i<=m;i++) {
		int x=read();
		printf("%lld\n",H.find(x));
	}
	return 0;
}
posted @ 2019-01-09 11:38  AKMer  阅读(333)  评论(0编辑  收藏  举报