码迷,mamicode.com
首页 > 其他好文 > 详细

[Bzoj 2956] 模积和 (整除分块)

时间:2019-01-13 11:00:01      阅读:189      评论:0      收藏:0      [点我收藏+]

标签:mes   求和   解法   argc   乘法分配律   string   分块   turn   char   

整除分块

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

题目解法

 化公式变成比较方便的形式:
  \(\ \sum_{i = 1}^n \sum_{j = 1}^m (n \mod i)(m \mod j), i \ne j\)
 \(= \sum_{i = 1}^n \sum_{j = 1}^m (n - i \lfloor \frac{n}{i} \rfloor)(m - j \lfloor \frac{m}{j} \rfloor) - \sum_{i = 1}^{min(n, m)} (n - i \lfloor \frac{n}{i} \rfloor)(m - i \lfloor \frac{m}{i} \rfloor)\)
 
 乘法分配律展开,化简,令 \(t = min(n, m)\) 得:
  \(\ \sum_{i = 1}^n \sum_{j = 1}^m (n - i \lfloor \frac{n}{i} \rfloor)(m - j \lfloor \frac{m}{j} \rfloor) - \sum_{i = 1}^t (n - i \lfloor \frac{n}{i} \rfloor)(m - i \lfloor \frac{m}{i} \rfloor)\)
 \(= \sum_{i = 1}^n \sum_{i = 1}^m nm + m\sum_{i = 1}^n \sum_{i = 1}^m j \lfloor \frac{n}{i} \rfloor + n\sum_{i = 1}^n \sum_{i = 1}^m \lfloor \frac{m}{j} \rfloor +\)
 \(\sum_{i = 1}^n \sum_{i = 1}^m ij \lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{j} \rfloor - \sum_{i = 1}^t nm + m \sum_{i = 1}^t i \lfloor \frac{n}{i} \rfloor - n \sum_{i = 1}^t i \lfloor \frac{m}{i} \rfloor - \sum_{i = 1}^t i^2 \lfloor \frac{n}{i} \rfloor\)
 \(\sum_{i = 1}^n \sum_{i = 1}^m ij \lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{j} \rfloor\) 等于 \(\sum_{i = 1}^n i \lfloor \frac{n}{i} \rfloor * \sum_{i = 1}^m j \lfloor \frac{m}{j} \rfloor\),一个一个算即可。
 
 代码写的比较长……因为用 \(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\n", (ans + sum_1 * sum_2) % mod);

  return 0;
}

[Bzoj 2956] 模积和 (整除分块)

标签:mes   求和   解法   argc   乘法分配律   string   分块   turn   char   

原文地址:https://www.cnblogs.com/nanjoqin/p/10261968.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!