Codeforces Round#769(Div.2) D. New Year Concert

题目

新年快到了,这意味着在179学校,音乐会的准备工作正在如火如荼地进行。

学校有n个班,从1到n编号,第i班准备了一个长度为ai分钟的场景。

作为负责举办音乐会的主要负责人,Idnar 知道,如果一场音乐会有 k 个场景,长度为 b1, b2, …, bk 分钟,那么如果存在两个整数 l 和 r 使得 1≤l≤ r≤k 且 gcd(bl,bl+1,…,br−1,br)=r−l+1,其中 gcd(bl,bl+1,…,br−1,br) 等于最大公约数数字 bl, bl+1, …, br−1, br 的除数 (GCD)。

为了避免让观众感到厌烦,Idnar 可以要求第 t 类 (1≤t≤k) 制作一个长度为 d 分钟的新场景的任意次数(可能为零),其中 d 可以是任何正整数。因此,在这个操作之后,bt 等于 d。请注意,每个操作的 t 和 d 可能不同。

对于一系列场景长度 b1, b2, …, bk,令 f(b) 为 Idnar 必须要求更改场景使得避免让观众感到厌烦的最小类数。

Idnar 尚未决定音乐会允许哪些场景,因此他想知道 a 的每个非空前缀的 f 值。换句话说,Idnar 想知道 f(a1), f(a1,a2), …, f(a1,a2,…,an) 的值

输入
第一行包含一个整数 n (1≤n≤2⋅105)——学校的班级数量。

第二行包含 n 个正整数 a1, a2, …, an (1≤ai≤109) — 类场景的长度。

输出
在一行中打印 n 个整数的序列 — f(a1), f(a1,a2), …, f(a1,a2,…,an)。

题解

假如我们遇到一个不合法的子串,我们一定会将某个数改变为极大的质数,这样就不会影响任何一个段,且包含这个数的段一定不存在gcd(bl,bl+1,…,br−1,br)=r−l+1,除非这个数本身等于1;所以我们记录上一个被修改的起点,只需要考虑一个新段的公约数即可,每次加入一个数就维护一下标记起点到这个起点中有没有不合法的值,用一个ST表来维护区间公约数即可。

#include<bits/stdc++.h>
#define maxn 200005
#define INF 2147483647
using namespace std;
int a[maxn][19],b[maxn];
int n;
int gcd(int l,int r){
	if(l>r) return INF;
	int k=b[r-l+1];
	return __gcd(a[l][k],a[r-(1<<k)+1][k]);
}
int main()
{
	cin>>n;
	int l=0,k=1;
	for(int i=2;i<=n;i++){
		if(i>=k*2) l++,k*=2;
		b[i]=l;
	}
	for(int i=0;i<n;i++)
		scanf("%d",&a[i][0]);
	for(int i=1;i<19;i++){
		for(int j=0;j<n;j++){
			if(j+(1<<i)<=n)
				a[j][i]=__gcd(a[j][i-1],a[j+(1<<(i-1))][i-1]);
		}
	}
	int ans=0;l=0;
	for(int r=0;r<n;r++){
		while((r-l+1)>=gcd(l,r)){
			if((r-l+1)==gcd(l,r)){
				l=r+1;ans++;
			}
			else l++;
		}
		cout<<ans<<" ";
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值