Cicada的序列

博客探讨了Cicada序列的优化方法,通过限制除法操作至最多log(n)次,将原本需要O(n)枚举的右端点问题转化为使用二分查找和ST表在O(log2n)时间内找到目标位置,从而降低总时间复杂度到O(nlog2n)。
摘要由CSDN通过智能技术生成

Cicada的序列
这道题貌似不太好做,但是我们发现除法最多只能除log(n)次,所以可以优化复杂度
具 体 的 , 原 来 右 端 点 需 要 O ( n ) 枚 举 的 , 我 们 可 以 先 二 分 出 一 段 区 间 , 用 s t 表 O ( 1 ) 查 本 区 间 的 最 值 , 找 到 第 一 个 比 原 数 小 的 位 置 , 因 为 只 能 找 l o g n 次 , 所 以 时 间 降 为 l o g 2 n , 总 时 间 复 杂 度 为 O ( n l o g 2 n ) 具体的,原来右端点需要O(n)枚举的,我们可以先二分出一段区间,用st表O(1)查本区间的最值, 找到第一个比原数小的位置,因为只能找logn次,所以时间降为log^2n,总时间复杂度为O(nlog^2n) OnstO(1)lognlog2n,O(nlog2n)


#include<bits/stdc++.h>

using namespace std;

typedef __int128 ll;
const int N=300010;
int n,st[N][20],a[N];//
int find(int l,int r){
	int len=log2(r-l+1);
	return min(st[l][len],st[r-(1<<len)+1][len]);
}
int get(int l,int r,int val){
	int ans=0;
	while(l<=r){
		int mid=(l+r)>>1;
		if(find(l,mid)<=val) ans=mid,r=mid-1;
		else l=mid+1;
	}
	return ans;
}
void print(ll x){
	if(x>=10){
		print(x/10);
	}
	putchar('0'+x%10);
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){scanf("%d",&a[i]);st[i][0]=a[i];}
	int up=log2(n);
	for(int len=1;len<=up;len++){
		for(int l=1;l+(1<<len)-1<=n;l++){
			st[l][len]=min(st[l][len-1],st[l+(1<<(len-1))][len-1]);
		}
	}
	ll ans=0;
	for(int i=1;i<=n;i++){
		int pos=i+1,val=a[i];ans+=val;
		while(pos<=n){
			int nxt=get(pos,n,val);
			if(nxt==0){
				ans+=1ll*(n-pos+1)*val;
				break;
			}
			ans+=1ll*(nxt-pos)*val;
			pos=nxt;
			val=val%a[nxt];
		}
	}
	print(ans);
}

除法最多logn次,所以用最值计算往往能优化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值