CGCDSSQ CodeForces - 475D(ST表&数论)

CGCDSSQ CodeForces - 475D(ST表&数论)

题目大意

给出一段序列与q个询问v,问存在多少的二元组 ( l , r ) (l,r) ,满足 g c d ( a l , a l + 1 , . . . a r ) = v gcd(a_l,a_{l+1},...a_{r})=v

解题思路

首先需要维护出一个ST表,其用于快速查询一段区间的gcd,然后对每个序列中的每个数,枚举所有以其为第一项的子序列的gcd,并用map保存其数量.但是直接暴力枚举,可能导致超时,这时需要用到一个性质,即从i开始向右,不断取共同的公因子,其至多会出现 l o g 2 ( a i ) log_2(a_i) 种答案,因为,每次取公因子操作,假如公因子发生了变化,则至少会/2.为此,只需要每次找出i往后第一个gcd发生了的点设为j即可.那么这一段的贡献就是 j i j-i 而这只需要二分进行搜索即可.因此这个问题总的复杂度就是 O ( n l o g 2 ( a m a x ) l o g 2 n ) O(nlog_2(a_{max})log_2n)

AC代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int size=1e5+5;
const int lgsz=log2(size)+1;
int a[size];
struct ST{
	int st[size][lgsz];
	void build(int *a,int len){
		for(int i=1;i<=len;i++) st[i][0]=a[i];
		for(int j=1;(1<<j)<=len;j++){
			for(int i=1;i+(1<<j)-1<=len;i++){
				st[i][j]=__gcd(st[i][j-1],st[i+(1<<(j-1))][j-1]);
			}
		}
	}
	int query(int l,int r){
		if(l==r) return st[l][0];
		int k=log2(r-l+1);
		return __gcd(st[l][k],st[r-(1<<k)+1][k]);
	}
}sstt;
unordered_map<int,int> mp;	
int find(int l,int r,int b,int val)
{
	int ans=r;
	while(l<=r)
	{
		int mid=(l+r)/2;
		if(sstt.query(b,mid)==val)
		{
			l=mid+1;
		}
		else 
		{
			r=mid-1;
			ans=min(ans,r);
		}
	}
	return ans;
}
int32_t main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	sstt.build(a,n);
	int gcd,loc;
	int pre;
	for(int i=1;i<=n;i++)
	{
		gcd=a[i];
		loc=i;
		while(true){
			pre=loc;
			loc=find(loc,n,i,gcd);
			gcd=sstt.query(i,loc);
			mp[gcd]+=loc-pre+1;
			if(loc<n){
				loc++;
				gcd=sstt.query(i,loc);
			}else break;
		}
	}
	int q;
	cin>>q;
	int x;
	while(q--){
		cin>>x;
		cout<<mp[x]<<endl;
	}
}

猜你喜欢

转载自blog.csdn.net/baiyifeifei/article/details/88062956
今日推荐