[BZOJ5358][Lydsy1805月赛]口算训练(质因数分解+离线乱搞)

Address

https://begin.lydsy.com/JudgeOnline/upload/201805.pdf

Solution

发现好多人用主席树
这里讲一种跑得更快的离线做法。
首先,对于每个询问 ( l , r , d ) ,可以将 d 分解质因数。
如果分解的结果是 d = i = 1 r p i q i ,并且质因子 x [ l , r ] 内所有数中出现的次数之和为 s u m [ l , r , x ] ,那么询问转化为,判断是否满足:

1 i r , s u m [ l , r , p i ] q i

尝试把每个数进行质因数分解(由于有多组数据,因此可以预处理出 [ 1 , 10 5 ] 内所有数的质因数分解以提高效率,而一个 [ 1 , 10 5 ] 的质因子个数不超过 6
把询问 s u m [ l , r , x ] 拆成 s u m [ 1 , l 1 , x ] s u m [ 1 , r , x ]
于是询问再次转化:求一个前缀内,质因子 x 的出现次数。
但可以发现,如果对于每个质因子都维护一个前缀和,那么空间是 O ( n 2 ) 的,开不下。
于是将询问离线,按照前缀的长度(末尾标号)从小到大排序。
用一个指针 i 将序列从左到右扫描一遍,扫描的过程中,维护前缀 [ 1 , i ] 内各个因子的出现次数。这样,当 i = k 的时候,就能立即回答形如 s u m [ k , x ] 的询问。

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
const int N = 1e5 + 5, M = N << 1, E = 10;
int n, m, w, a[N], tot, pri[N], pr[N], num[N][E], pw[N][E], qn[N], cnt[N],
ans[N][E]; bool mark[N]; struct cyx {int i, id;} q[M];
inline bool comp(const cyx &a, const cyx &b) {return a.i < b.i;}
void sieve() {
    int i, j; mark[0] = mark[1] = 1; For (i, 2, 100000) {
        if (!mark[i]) pri[++tot] = i; For (j, 1, tot) {
            if (1ll * i * pri[j] > 100000) break; mark[i * pri[j]] = 1;
            if (i % pri[j] == 0) break;
        }
    }
    For (i, 1, 100000) {
        int x = i; For (j, 1, tot) {
            int y = pri[j]; if (1ll * y * y > i) break;
            if (x % y) continue; num[i][++pr[i]] = y;
            while (x % y == 0) pw[i][pr[i]]++, x /= y;
        }
        if (x > 1) num[i][++pr[i]] = x, pw[i][pr[i]] = 1;
    }
}
void work() {
    int i, j, l, r; n = read(); m = read(); For (i, 1, n) a[i] = read(); w = 0;
    For (i, 1, m) {
        For (j, 1, 6) ans[i][j] = 0; l = read(); r = read(); qn[i] = read();
        q[++w] = (cyx) {l - 1, w - 1}; q[++w] = (cyx) {r, w - 1};
    }
    sort(q + 1, q + w + 1, comp); memset(cnt, 0, sizeof(cnt)); l = 1;
    For (i, 0, n) {
        if (i) For (j, 1, pr[a[i]]) cnt[num[a[i]][j]] += pw[a[i]][j];
        while (l <= w && q[l].i == i) {
            int id = (q[l].id >> 1) + 1; For (j, 1, pr[qn[id]])
                if (q[l].id & 1) ans[id][j] += cnt[num[qn[id]][j]];
                else ans[id][j] -= cnt[num[qn[id]][j]]; l++;
        }
    }
    For (i, 1, m) {
        double res = 1; For (j, 1, pr[qn[i]])
            res = res && ans[i][j] >= pw[qn[i]][j]; puts(res ? "Yes" : "No");
    }
}
int main() {
    int T = read(); sieve(); while (T--) work(); return 0;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/80640764