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

    [CQOI2007]余数求和

    洛谷

    BZOJ

    题目大意:求 ni=1k mod i∑i=1nk mod i 的值。

    等等……这题就学了三天C++的都会吧?

    1n,k1091≤n,k≤109。(一口老血喷到屏幕上)

    O(n)O(n) 行不通了,考虑别的做法。


    我们来看一下 xi⌊xi⌋ 的值。

    x=9x=9:(不包括0,只有4种取值?)

    i

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

    0

    x=12x=12:(不包括0,只有6种取值?)

    i 1 2 3 4 5 6 7 8 9 10 11 12
    x/i 12 6 4 3 2 2 1 1 1 1 1

    1

    貌似 xi⌊xi⌋ 取值数不是很多?

    我们来估算一下 xi⌊xi⌋ 的不同取值个数:

    当 1ix−−√1≤i≤⌊x⌋ 时,ii 都只有 x−−√⌊x⌋ 个,不同的取值数肯定不会更多。

    当 x−−√ix⌊x⌋≤i≤x 时,1xix−−√1≤⌊xi⌋≤⌊x⌋,不同的取值数肯定 x−−√≤⌊x⌋ 个。

    综上,不同取值数是 x−−√x 级别的。

    然后我们可以发现相同的数是连续的一段。那么我们可以通过这个特点把 xi⌊xi⌋ 分成几段,每一段的数相等,那么这一段的和就是长度 ××这个相同的数。

    因为不同取值只有 x−−√x 个,所以这样加速后的时间复杂度是 O(x−−√)O(x),比 O(x)O(x) 快了不少。这就是整除分块。


    回到原题。

     求 ni=1k mod i∑i=1nk mod i 的值。

    这个……看着和整除分块没什么大关系的样子?

    我们看这个 modmod 真碍眼,把它拆开。

    k mod i=ki×kik mod i=k−i×⌊ki⌋

    那么就有:

     ni=1k mod i ∑i=1nk mod i

    =ni=1ki×ki=∑i=1nk−i×⌊ki⌋

    =nkni=1i×ki=nk−∑i=1ni×⌊ki⌋

    后面这个式子貌似可以整除分块了……怎么算呢?

    我们考虑 [l,r][l,r] 这段区间的求和,其中 ki=x:i[l,r]⌊ki⌋=x:i∈[l,r]。

     ri=li×ki ∑i=lri×⌊ki⌋

    =ri=li×x=∑i=lri×x

    =xri=li=x∑i=lri

    =x(l+r)(rl+1)2=x(l+r)(r−l+1)2

    这样就不是很难了。


    话说讲了这么久也没讲怎么枚举一段相同区间的左端点和右端点。

    我们这样扫描:

    一开始 l=1l=1 显而易见。

    求出对应的 rr。

    这个区间求完了,下一个 ll 应该是下一个还没扫过的位置,即 l=r+1l=r+1。

    一直重复直到 ll 到了上界,也就是扫完了。

    怎么求对应的 rr 呢?

    既然 kl=kr⌊kl⌋=⌊kr⌋,且 rr 是右端点(最大)

    那么 r=kklr=kkl。(当然可能要跟枚举上界取一个min,视情况而定)

    整除分块模板大概如下:

    1 for(int l=1,r;l<=n;l=r+1){
    2     r=n/(n/l);
    3     //do something...
    4 }

    那么这题代码实现就不难了。需要注意本题有不少坑点,详见代码。(没错,代码并没有你想象的那么长!)

    时间复杂度貌似是 O(min(k,n)−−−−−−−−√)O(min(k,n)),空间复杂度 O(1)

    #include<iostream>
    #include<cmath>
    using namespace std;
    typedef long long ll;    //long long是需要的
    ll n,k,ans;
    int main(){
        cin>>n>>k;
        ans=n*k;
        for(ll l=1,r;l<=min(n,k);l=r+1){    //与上界取min!
            r=min(k/(k/l),n);    //与上界取min!
            ans-=(k/l)*(l+r)*(r-l+1)/2;
        }
        cout<<ans<<endl;
    }
    

      

  • 相关阅读:
    ASP.NET MVC 3: Razor中的@:和语法
    telerik 值得学习 web mvc 桌面 控件大全
    Android 基于google Zxing实现对手机中的二维码进行扫描
    Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果
    SQL聚集索引和非聚集索引的区别
    SQL Server的聚集索引和非聚集索引
    请教一个Jquery ligerui 框架的小问题
    学习如何用VS2010创建ocx控件
    nginx-rtmp-module--------------WIKI
    rtmp一些状态信息详解-as连接FMS服务器报错状态汇总~~
  • 原文地址:https://www.cnblogs.com/bytebull/p/9324317.html
Copyright © 2011-2022 走看看