珂朵莉的约数
來源:??途W:
題目描述
珂朵莉給你一個長為n的序列,有m次查詢
每次查詢給兩個數l,r
設s為區間[l,r]內所有數的乘積
求s的約數個數mod 1000000007
輸入描述:
第一行兩個正整數n,m
第二行一個長為n的序列
之后m行每行兩個數l和r
輸出描述:
對于每個詢問,輸出一個整數表示答案
示例1
輸入
復制
輸出
復制
備注:
對于100%的數據,有n , m <= 100000 , a[i] <= 1000000
題解:
莫隊+數論(約數個數)
很容易看出是莫隊的題,個人認為難點在于求約數的個數,常規的求約束的個數肯定不行,有一個叫約束個數定理的東西
任何一個大于1的n都可以分解:
n=p1a1×p2a2×p3a3*…*pkak,p為素數
而n的約數的個數就是(a1+1)(a2+1)(a3+1)…(ak+1)
再看一下本題數據,對于不超過1000的素數我們可以直接維護每個素數的冪指數+1的前綴乘積(共168個),而超過1000的素因數最多也就一個。代碼中ant用來記錄1000之外的素因子,res用來記錄1000之內的每個素數的冪指數+ 1 +1+1的前綴乘積
線性篩就是每一次被最小素因數給篩出來,所以用線性篩來計算因數和。因為題目要mod,所以我們還要線性預處理逆元,方便后面使用
具體線性篩如何求因數和可以看其他博客講解
具體代碼如下
代碼:
#include <bits/stdc++.h>using namespace std; typedef long long ll; const int N = 1e5 + 10; const int MOD = 1e9+7;int prime[1007],tot; bool vis[1007]; ll inv[N],ans[N],ant; int Be[N],a[N],sum[N*10],pre[N][170]; // sum存 在區間內 > 1000 的素數的個數 void init() {tot = 0;for(int i = 2;i <= 1000;i++){if(vis[i]) continue;prime[tot++] = i;for(int j = i + i; j <= 1000; j += i )vis[j] = 1;} }struct Mo{int l,r,id; }Q[N]; int cmp(Mo a,Mo b){ return Be[a.l] == Be[b.l] ? a.r < b.r : a.l < b.l;}void add(int pos) {if(a[pos] == 1) return;ant = ant*inv[1+sum[a[pos]]]%MOD;sum[a[pos]]++;ant = ant*(1+sum[a[pos]])%MOD; } void del(int pos) {if(a[pos] == 1) return;ant = ant*inv[1+sum[a[pos]]]%MOD;sum[a[pos]]--;ant = ant*(1+sum[a[pos]])%MOD; } int main() {int n,m;init();scanf("%d%d",&n,&m);inv[0] = inv[1] = 1; for(int i=2;i<=n+1;i++) inv[i]=1ll*(MOD-MOD/i)*inv[MOD%i]%MOD ; // 逆元篩int len = sqrt(n);for(int i = 1;i <= n;i++){scanf("%d",&a[i]);Be[i] = i/len;for(int j = 0;j < tot;j++){pre[i][j] = pre[i-1][j];while(a[i] % prime[j] == 0)//如果這個質數是因子 {pre[i][j]++;a[i] /= prime[j]; }}}for(int i = 1;i <= m;i++) scanf("%d%d",&Q[i].l,&Q[i].r),Q[i].id = i;sort(Q+1,Q+m+1,cmp);memset(sum,0,sizeof(sum));ant = 1;int l = 1,r = 0;for(int i = 1;i <= m;i++){while(r < Q[i].r) add(r+1),r++;while(r > Q[i].r) del(r),r--;while(l < Q[i].l) del(l),l++;while(l > Q[i].l) add(l-1),l--;ll res = 1;for(int j=0;j<tot;j++)res=1ll*res*(pre[r][j]-pre[l-1][j]+1)%MOD;ans[Q[i].id] = 1ll*ant*res%MOD;}for(int i = 1;i <= m;i++)printf("%lld\n",ans[i]);return 0; }總結
- 上一篇: 如何修复手机电池 这样操作焕然一新
- 下一篇: [数论]线性筛——约数个数与约数和