椰子 题解
BuQiuRu4587 · · 个人记录
以下是官方题解:
首先看懂题后可以获得一个十分简单的暴力做法:
枚举那个被修改的数字,然后枚举每一个区间,计算区间
时间复杂度为
然后考虑一个比较送的性质 A,用上面的代码打表可以很快发现答案是
送分结束。
首先我们应该发现一件事,对于每个答案,它都必然是
问题变为判断:在区间不经过值为
既然要使得一个区间
我们考虑优化刚刚的整个过程,由于每次都要扫描整个序列,而序列中不是
现在我们来分析这个做法的复杂度,对于每一轮到
代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,a[500001],pos[500001];
int fa[500001],val[500001];
int find(int x){ //并查集
if(x==fa[x]) return x;
return fa[x]=find(fa[x]);
}
void merge(int x,int y){ //合并两段
x=find(x),y=find(y);
if(x==y) return ;
val[y]=__gcd(val[x],val[y]); //更新这一段的 gcd 值
fa[x]=y;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
pos[a[i]]=i;
}
for(int i=1;i<=n;i++) fa[i]=i,val[i]=a[i];
cout<<n<<' ';//改 1 没有影响,答案一定是 n
for(int i=2;i<=n;i++){ //求解修改 i 时的答案
int ans=n+1;
for(int j=i+i;j<=n;j+=i){
int x=pos[j];
if(x!=1&&a[x-1]%i==0&&a[x-1]!=i) merge(x-1,x); //可以和左右合并就直接合并两个 i 的倍数构成的块
if(x!=n&&a[x+1]%i==0&&a[x+1]!=i) merge(x+1,x);
ans=min(ans,val[find(x)]); //查询当前情况 i 的倍数段中最小的 gcd 值
}
if(ans==i) cout<<n<<' '; //gcd可以为 i
else cout<<n-1<<' ';
for(int j=i+i;j<=n;j+=i) fa[pos[j]]=pos[j],val[pos[j]]=j; // 清空
}
}