zoukankan      html  css  js  c++  java
  • [Bzoj 2956] 模积和 (整除分块)

    整除分块

     一般形式:(sum_{i = 1}^n lfloor frac{n}{i} floor * f(i))
     需要一种高效求得函数 (f(i)) 的前缀和的方法,比如等差等比数列求和或对于积性函数的筛法等,然后就可以用整除分块的思想做。
     

    题目解法

     化公式变成比较方便的形式:
      ( sum_{i = 1}^n sum_{j = 1}^m (n mod i)(m mod j), i e j)
     (= sum_{i = 1}^n sum_{j = 1}^m (n - i lfloor frac{n}{i} floor)(m - j lfloor frac{m}{j} floor) - sum_{i = 1}^{min(n, m)} (n - i lfloor frac{n}{i} floor)(m - i lfloor frac{m}{i} floor))
     
     乘法分配律展开,化简,令 (t = min(n, m)) 得:
      ( sum_{i = 1}^n sum_{j = 1}^m (n - i lfloor frac{n}{i} floor)(m - j lfloor frac{m}{j} floor) - sum_{i = 1}^t (n - i lfloor frac{n}{i} floor)(m - i lfloor frac{m}{i} floor))
     (= sum_{i = 1}^n sum_{i = 1}^m nm + msum_{i = 1}^n sum_{i = 1}^m j lfloor frac{n}{i} floor + nsum_{i = 1}^n sum_{i = 1}^m lfloor frac{m}{j} floor +)
     (sum_{i = 1}^n sum_{i = 1}^m ij lfloor frac{n}{i} floor lfloor frac{m}{j} floor - sum_{i = 1}^t nm + m sum_{i = 1}^t i lfloor frac{n}{i} floor - n sum_{i = 1}^t i lfloor frac{m}{i} floor - sum_{i = 1}^t i^2 lfloor frac{n}{i} floor)
     (sum_{i = 1}^n sum_{i = 1}^m ij lfloor frac{n}{i} floor lfloor frac{m}{j} floor) 等于 (sum_{i = 1}^n i lfloor frac{n}{i} floor * sum_{i = 1}^m j lfloor frac{m}{j} floor),一个一个算即可。
     
     代码写的比较长……因为用 (unsigned ll) 为了避免出负数也多了很多取模……

    #include <queue>
    #include <cstdio>
    #include <cctype>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    typedef unsigned long long u64;
    
    const u64 mod = 19940417;
    const u64 inv_6 = 3323403;
    
    inline u64 Calc_1(u64 l, u64 r) { return (l + r) * (r - l + 1) / 2 % mod; }
    inline u64 Calc_2(u64 x) { return ((x + 1) * (2 * x + 1) % mod) * (x * inv_6 % mod) % mod; }
    
    int main(int argc, const char *argv[])
    {
      u64 n = 0, m = 0, t = 0, ans = 0, sum_1 = 0, sum_2 = 0;
      scanf("%llu%llu", &n, &m);
      ans = ((n * m % mod) * (n * m % mod) % mod);
      t = min(n, m), ans = (ans + mod - (n * m % mod) * t % mod) % mod;
      for(u64 tmp, l = 1, r = 1; l <= n; l = r + 1) {
        tmp = n / l, r = n / tmp;
        ans = (ans + mod - (Calc_1(l, r) * tmp % mod) * (m * m % mod) % mod) % mod;
        sum_1 = (sum_1 + Calc_1(l, r) * tmp % mod) % mod;
      }
      for(u64 tmp, l = 1, r = 1; l <= m; l = r + 1) {
        tmp = m / l, r = m / tmp;
        ans = (ans + mod - (Calc_1(l, r) * tmp % mod) * (n * n % mod) % mod) % mod;
        sum_2 = (sum_2 + Calc_1(l, r) * tmp % mod) % mod;
      }
      for(u64 tmp, l = 1, r = 1; l <= t; l = r + 1) {
        tmp = n / l, r = min(t, n / tmp);
        ans = (ans + Calc_1(l, r) * (tmp * m % mod)) % mod;
      }
      for(u64 tmp, l = 1, r = 1; l <= t; l = r + 1) {
        tmp = m / l, r = min(t, m / tmp);
        ans = (ans + Calc_1(l, r) * (tmp * n % mod)) % mod;
      }
      for(u64 l = 1, r = 1; l <= t; l = r + 1) {
        r = min(t, min(n / (n / l), m / (m / l)));
        ans = (ans + mod - (mod + Calc_2(r) - Calc_2(l - 1)) * ((n / l) * (m / l) % mod) % mod) % mod;
      }
      printf("%llu
    ", (ans + sum_1 * sum_2) % mod);
    
      return 0;
    }
    
  • 相关阅读:
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 大臣的旅费
    Java实现 蓝桥杯 历届试题 大臣的旅费
    Java实现 蓝桥杯 历届试题 大臣的旅费
    Java实现 蓝桥杯 历届试题 大臣的旅费
    Navicat查询哪些表有指定字段名
  • 原文地址:https://www.cnblogs.com/nanjoqin/p/10261968.html
Copyright © 2011-2022 走看看