Cicada的序列

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

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次,所以用最值计算往往能优化

你可能感兴趣的:(例题,二分,优化)