zoukankan      html  css  js  c++  java
  • P2261 [CQOI2007]余数求和[整除分块]

    题目大意

    给出正整数 n 和 k 计算 (G(n, k)=k mod 1 + k mod 2 + k mod 3 + cdots + k mod n) 的值 其中 (k mod i) 表示 k 除以 i 的余数。

    解析

    整除分块的一个典型例子。


    整除分块解决的是形如

    [sum^n_{i=1} ~ lfloorfrac{n}{i} floor ]

    的问题,其复杂度为(O(sqrt{n}))

    实际上是规律性的一类问题,打表可以发现对于一些连续的(i)(lfloorfrac{n}{i} floor)具有相同的值。具体而言,对于一个(i),在一个区间(isim lfloorfrac{n}{lfloorfrac{n}{i} floor} floor)中,(lfloorfrac{n}{i} floor)具有相同的值。

    也就是说,对于这个问题,我们只需要把整除分块中每一块的和累加就行了。


    回到这道题,把题意转化为数学语言

    [sum_{i=1}^n ~ k mod i ]

    根据模算术的定义,可以写成

    [sum_{i=1}^n ~ k-lfloorfrac{k}{i} floor*i ]

    [n*k-sum_{i=1}^n ~ lfloorfrac{k}{i} floor*i ]

    后面的东西就是整除分块,对于一个块(lsim r),有

    [(r-l+1)*k-(r-l+1)lfloorfrac{k}{i} floor*sum_{i=l}^r ~ i ]

    所以对于一个块,(sum_{i=l}^r ~ i) 实际上是一个等差数列,我们一并求出来就可以了。

    参考代码

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<ctime>
    #include<cstdlib>
    #include<algorithm>
    #include<queue>
    #include<set>
    #include<map>
    #define ll long long
    using namespace std;
    ll n,k;
    int main()
    {
    	scanf("%d%d",&n,&k);
    	ll tmp=0;
    	for(int l=1,r=0;l<=n;l=r+1){
    		if(!(k/l)) r=n;
    		else r=min(k/(k/l),n);
    		tmp+=(r-l+1)*(k/l)*(l+r)/2;
    	}
    	printf("%lld
    ",n*k-tmp);
    	return 0;
    }
    
  • 相关阅读:
    linux 环境变量
    Java finally语句到底是在return之前还是之后执行?
    JAVA NIO之浅谈内存映射文件原理与DirectMemory
    cpu架构
    tomcat 安装
    linux 下载rpm包到本地,createrepo:创建本地YUM源
    linux下查看和添加PATH环境变量
    virtualbox 相关操作
    空间叠加分析
    java程序的加载与执行
  • 原文地址:https://www.cnblogs.com/DarkValkyrie/p/11440608.html
Copyright © 2011-2022 走看看