题目描述
珂朵莉给你一个长为n的序列,有m次查询
每次查询给两个数l,r
设s为区间[l,r]内所有数的乘积
求s的约数个数mod 1000000007
输入描述:
第一行两个正整数n,m
第二行一个长为n的序列
之后m行每行两个数l和r
输出描述:
对于每个询问,输出一个整数表示答案
示例1
输入
5 5 64 2 18 9 100 1 5 2 4 2 3 1 4 3 4
输出
165 15 9 45 10
备注:
对于100%的数据,有n , m <= 100000 , a[i] <= 1000000
题解
莫队算法,质因数分解。
本质就是求区间内每个素因子出现的次数的乘积,可以用莫队来搞。
然后超时了......分析了一波发现每个数最多可以分解成$20$个左右的素因子,也就是莫队增加一个数删除一个数的时候,需要进行$20$个数的修改。
常数有点大。又分析了一波,发现每个数最多只能分解成$7$个素因子,也就是按种类来修改可以减少一些常数。还是超时......
最后看了题解,发现好强啊......:
对每个数进行质因子分解,考虑一个数的约数个数即为其每个质因子出现次数+1的乘积,所以维护这个即可
考虑每个数只有一个大于1000的质数,对这部分进行根号分治
对于小于1000的质因子(只有168个),维护一个前缀和pre[i][j]表示第i个质因子在前j个数中出现次数
对于大于1000的质因子,用莫队维护即可
这样操作,莫队转移复杂度大大降低,小的素因子一波搞就$ok$了。
然后写莫队的时候发现了曾经没有注意到的事情,就是要先进行$add$,然后进行$delete$。不举例了。
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 10; const long long mod = 1000000007LL; int a[maxn]; int b[maxn]; int sum[maxn][200]; int c[50], g; int cnt[10 * maxn]; int pos[maxn]; int n, m, L, R; long long Ans; struct X { int l, r, id; }s[maxn]; long long ans[maxn]; bool cmp(const X& a, const X& b) { if (pos[a.l] != pos[b.l]) return a.l < b.l; if((pos[a.l]) & 1) return a.r > b.r; return a.r < b.r; } bool noprime[10 * maxn]; int prime[10 * maxn], num_prime; long long inv[20 * maxn]; void init() { inv[0] = inv[1] = 1; for(long long i = 2; i <= 2000005; i ++) { inv[i] = inv[mod % i] * (mod - mod / i) % mod; } noprime[1] = 1; for(int i = 2; i <= 1000000; i ++) { if(noprime[i]) continue; prime[num_prime ++] = i; for(int j = i + i ; j <= 1000000; j = j + i) { noprime[j] = 1; } } } void add(int x) { if(x == 0) return; Ans = Ans * inv[cnt[x] + 1] % mod; cnt[x] += 1; Ans = Ans * (cnt[x] + 1) % mod; } void del(int x) { if(x == 0) return; Ans = Ans * inv[cnt[x] + 1] % mod; cnt[x] -= 1; Ans = Ans * (cnt[x] + 1) % mod; } int main() { init(); scanf("%d%d", &n, &m); int sz = sqrt(n); for(int i = 1; i <= n; i ++) { scanf("%d", &a[i]); pos[i] = i / sz; int now = 0; int tmp = a[i]; g = 0; while(tmp != 1) { if(noprime[tmp] == 0) { c[g ++] = tmp; tmp = 1; } else { while(tmp % prime[now] == 0) { c[g ++] = prime[now]; tmp = tmp / prime[now]; } now ++; } } for(int j = 0; j < g; j ++) { if(c[j] > 1000) { b[i] = c[j]; } else { for(int k = 0; k < 200; k ++) { if(prime[k] == c[j]) { sum[i][k] ++; } } } } } for(int i = 1; i <= n; i ++) { for(int j = 0; j < 200; j ++) { sum[i][j] = sum[i][j] + sum[i - 1][j]; } } for(int i = 1; i <= m; i ++) { scanf("%d%d", &s[i].l, &s[i].r); s[i].id = i; } sort(s + 1, s + m + 1, cmp); Ans = 1LL; for(int i = s[1].l; i <= s[1].r; i ++) { add(b[i]); } for(int j = 0; j < 200; j ++) { Ans = Ans * (sum[s[1].r][j] - sum[s[1].l - 1][j] + 1) % mod; } ans[s[1].id] = Ans; L = s[1].l; R = s[1].r; for(int i = 2; i <= m; i ++) { while (L > s[i].l) { L--, add(b[L]); } while (R < s[i].r) { R++, add(b[R]); } while (L < s[i].l) { del(b[L]), L++; } while (R > s[i].r) { del(b[R]), R--; } for(int j = 0; j < 200; j ++) { Ans = Ans * inv[sum[s[i - 1].r][j] - sum[s[i - 1].l - 1][j] + 1] % mod; Ans = Ans * (sum[s[i].r][j] - sum[s[i].l - 1][j] + 1) % mod; } ans[s[i].id] = Ans; } for(int i = 1; i <= m; i ++) { printf("%lld\n", ans[i]); } return 0; }