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;
    }
    

      

  • 相关阅读:
    Android 2.2 r1 API 中文文档系列(11) —— RadioButton
    Android API 中文 (15) —— GridView
    Android 中文 API (16) —— AnalogClock
    Android2.2 API 中文文档系列(7) —— ImageButton
    Android2.2 API 中文文档系列(6) —— ImageView
    Android 2.2 r1 API 中文文档系列(12) —— Button
    Android2.2 API 中文文档系列(8) —— QuickContactBadge
    [Android1.5]TextView跑马灯效果
    [Android1.5]ActivityManager: [1] Killed am start n
    Android API 中文(14) —— ViewStub
  • 原文地址:https://www.cnblogs.com/bytebull/p/9324317.html
Copyright © 2011-2022 走看看