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

本文详细解析了CodeForces-475D问题的解决策略,利用ST表加速区间gcd查询,结合数论知识优化枚举过程,通过二分查找提高效率。介绍了AC代码实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

题目大意

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

解题思路

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

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;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值