牛客小白月赛81 F题解

想了半个多小时才发现原来和之前Codeforces div3的最后一题是一个题,于是写一遍加深印象


题面

给一个长度为 的数组

对数组进行多次查询,每次查询数组的一个连续区间 ,需要计算集合 的大小

一开始以为是预处理,想了很久不知道怎么搞,想过st表,线段树,但感觉都做不了

突然发现后缀的 收敛的很快,非常快收敛到1之后就不用计算了,因为只用求不同的后缀gcd的个数,并且这样的后缀gcd是单调不增的

然后想到后缀gcd可能收敛的特别快,以至于可以改变复杂度

想了一下发现是log的速度,设目前的后缀gcd为 ,如果 对gcd有贡献,那么 分解成质因数幂的形式之后一定比后缀gcd的质因数幂中某一项的指数更小,那么有贡献的 的数量级是

于是暴力就是对的

但是需要注意一下没有贡献的 ,如果 没有贡献,那么满足 ,如果 一直比 大,那么这样的 不会很多(指数增长),但是相等的可能会很多。

而相等的一串是可以直接跳过去的,采用静态链表跳转,保证了复杂度是 级别的,可以过

AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int long long
typedef unsigned long long ull;
#define dmp(x) cerr<<"DEBUG"<<__LINE__<<":"<<#x<<" "<<x<<endl
const ll INF=0x3f3f3f3f3f3f3f3fLL;
typedef pair<int,int> pii;
const int N=6e5+5;
int a[N],lt[N];
signed main() {
    ios::sync_with_stdio(false);cin.tie(0);
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    for(int i=1;i<=n;i++)
        lt[i]=i-1;
    for(int i=1;i<=n;i++){
        if(a[i-1]%a[i]==0)
            lt[i]=lt[i-1];
    }
    for(int i=1;i<=m;i++){
        int l,r;
        cin>>l>>r;
        int ans=1,now=a[r];
        for(int j=r;j>=l;j=lt[j]){
            int tmp=__gcd(now,a[j]);
            if(tmp!=now)
                ans++;
            now=tmp;
            if(now==1)
                break;
        }
        cout<<ans<<'\n';
    }
    return 0;
}

img_show