zoukankan      html  css  js  c++  java
  • 牛客练习赛9 F

    题目描述

    珂朵莉给你一个长为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
    ", ans[i]);
      }
      return 0;
    }
    

     

  • 相关阅读:
    [ 转载 ] Mysql 远程连接+开放80和3306端口 常用配置
    [ 转载 ] Mysql 数据库常用命令
    [ 转载 ] Centos 安装mysql后启动失败 出现 ERROR 2002 (HY000): Can’t connect to local MySQL server through socket ‘/var/lib/mysql/mysql.sock’
    [2020多校联考]手套
    [2020多校联考]糖果机器
    [2020多校联考]染色相邻的边
    [2020多校联考]四个质数的和
    [2020多校联考]简单题
    [2020多校联考]MC
    [2020多校联考]进化
  • 原文地址:https://www.cnblogs.com/zufezzt/p/8151313.html
Copyright © 2011-2022 走看看