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;
    }
    
  • 相关阅读:
    Attributes in C#
    asp.net C# 时间格式大全
    UVA 10518 How Many Calls?
    UVA 10303 How Many Trees?
    UVA 991 Safe Salutations
    UVA 10862 Connect the Cable Wires
    UVA 10417 Gift Exchanging
    UVA 10229 Modular Fibonacci
    UVA 10079 Pizza Cutting
    UVA 10334 Ray Through Glasses
  • 原文地址:https://www.cnblogs.com/DarkValkyrie/p/11440608.html
Copyright © 2011-2022 走看看