cf475 D. CGCDSSQ

题意:

给定数组,q次询问,每次询问 x,求 \(\gcd(a_l,\dots, a_r)=x\)\(<l,r>\) 对的数量

思路:

只需要注意到当左端点固定时 gcd 递减且不同的 gcd 只有 log 个

法一:

预处理:固定左端点,每次找 gcd 相同的一段,一段一段地跳

区间 gcd 用 st 表搞

int erfen(int L, int g, int l, int r) {
    while(l < r) { //在[l,r]中找gcd[L,R]>=g的最大R
        int mid = l + r + 1 >> 1;
        if(ask(L, mid) >= g) l = mid; else r = mid - 1;
    }
    return l;
}
void sol() {
    cin >> n; for(int i = 1; i <= n; i++) cin >> a[i];
    initST();
    map<int, ll> ans;
    for(int l = 1; l <= n; l++)
        for(int g = 0, r1 = l, r2; r1 <= n; r1 = r2 + 1) //跳
            g = __gcd(g, a[r1]),
            r2 = erfen(l, g, r1, n), //gcd[l,r1]=gcd[l,r2]=g
            ans[g] += r2-r1+1;

    int q; cin >> q; while(q--) {
        int x; cin >> x; cout << ans[x] << endl;
    }
}

法二:超简单写法

\(a_i\) 结尾的区间的 gcd 要么是 \(a_i\)(区间长度为1),要么从前面以 \(a_{i-1}\) 结尾的区间的 gcd(记为 \(g'\))继承即 \(\gcd(g',a_i)\)

开 map 转移即可。map 记录以 \(a_i\) 结尾的所有区间的不同 gcd 的数量,不会超过 log

void sol() {
    int n; cin >> n;

    map<int, ll> ans, mp[2];
    
    for(int i = 1; i <= n; i++) {
        mp[i&1].clear();
        int x; cin >> x;
        mp[i&1][x]++;
        for(auto &[g,c]: mp[(i-1)&1])
            mp[i&1][__gcd(g, x)] += c;
        
        for(auto &[g,c]: mp[i&1]) ans[g] += c;
    }

    int q; cin >> q; while(q--) {
        int x; cin >> x; cout << ans[x] << endl;
    }
}
posted @ 2022-06-17 13:26  Bellala  阅读(37)  评论(0)    收藏  举报