zoukankan      html  css  js  c++  java
  • 除法分块

    计算一个式子:(sumlimits_{i = 1}^n cfrac{n}{i})
    很明显可以直接一个(for)循环,(O(n))求出结果,但是我们可以将其优化到(O(sqrt n))

    例题 AcWing199. 余数之和

    给定正整数n和k,计算((k mod 1) + (k mod 1) + ... + (k mod n))的值。 (1 leq n, k leq 10^9)

    首先我们知道(k mod i = k - lfloor cfrac{k}{i} floor * i)
    所以上边式子可以化简为,(n * k - sumlimits_{i = 1}^n lfloor cfrac{k}{i} floor * i),所以我们最主要的就是处理(lfloor cfrac{k}{i} floor)

    首先对于(n = 10)(k = 5)打个表,(i in [1, n]):(渲染之后,表格糊了,凑合看吧)

    k 1 2 3 4 5 6 7 8 9 10
    k/i 5 2 1 1 1 0 0 0 0 0

    很明显,我们发现(lfloor cfrac{k}{i} floor)的值呈区间变化,而除法分块就是让我们可以快速算出每一个区间的(l)(r)端点,然后计算即可。

    对于每一个区间,左端点(x = gx + 1)我们可以直接更新,然后对于右端点,先说结论,(gx = lfloor k / lfloor k / x floor floor)

    为什么呢?
    对于(x in [1, k],)我们设(g(x) = lfloor k / lfloor k / x floor floor)。首先明白(f(x) = k / x)是一个单调递减函数,然后我们现在来构造,(g(x) = lfloor k / lfloor k / x floor floor geq) (lfloor k/(k / x) floor),那么(lfloor k / g(x) floor leq lfloor k / x floor)

    再证明(lfloor k / g(x) floor geq lfloor k / x floor)

    (lfloor k / g(x) floor geq lfloor k / (k / lfloor k / x floor) floor)

    综上,得(lfloor k / g(x) floor = lfloor k / x floor)

    所以对于,(forall i in [x, lfloor k / lfloor k / x floor floor])(lfloor k / i floor)的值都相等。

    现在可以求区间端点的值了,下边证明为什么是(O(sqrt n))
    (i leq sqrt n)时,(lfloor k / i floor)最多有(sqrt k)个不同的值,而当(i > sqrt k)时,(lfloor k / i floor < sqrt k),所以(lfloor k / i floor)最多也只有(sqrt k)个不同的值,那么现在就明白了,我们上面分的块,最多有(2 sqrt k)块,因此就是(O(sqrt n))级别。

    对于题目要求(sumlimits_{i = l}^r lfloor cfrac{k}{i} floor * i) = (sumlimits_{i = l}^r lfloor cfrac{k}{l} floor * i) = (lfloor cfrac{k}{l} floor sumlimits_{i = l}^r i),后边用等差数列求和公式即可。

    // Problem: 余数之和
    // Contest: AcWing
    // URL: https://www.acwing.com/problem/content/201/
    // Memory Limit: 64 MB
    // Time Limit: 1000 ms
    // 
    // Powered by CP Editor (https://cpeditor.org)
    
    #include <bits/stdc++.h>
    
    using namespace std;
    
    int main() {
    	long long n, k;
    	cin >> n >> k;
    	long long res = n * k;
    	for (int x = 1, gx; x <= n; x = gx + 1) {
    		if (k / x == 0) gx = n;
    		else gx = min(k / (k / x), n);
    		res -= (k / x) * (x + gx) * (gx - x + 1) / 2;
    	}
    	
    	cout << res << endl;
    	
        return 0;
    }
    
  • 相关阅读:
    26_为什么我的删除刷新没有办法删除动态添加的数据呢?
    075_not in (null):这代表是什么意思呢?
    074_form表单中的value值
    025_回调函数没有遍历出数据
    073_模糊查询
    072_jsp追加与刷新数据
    071_为什么要catch return.setCode()?
    070_jstl中的三目表达式
    069_都是查询语句时得事物?
    打印周报模板
  • 原文地址:https://www.cnblogs.com/ZhengLijie/p/15487777.html
Copyright © 2011-2022 走看看