zoukankan      html  css  js  c++  java
  • P2260 [清华集训2012]模积和 题解

    CSDN同步

    原题链接

    简要题意:

    给定 (n,m),求:

    [sum_{i=1}^n sum_{j=1}^m (n space ext{mod} space i) imes (m space ext{mod} space j) , i ot = j ]

    (n,m leq 10^9).

    我们直接奔着 (100) 分的数据去吧,不要看部分分,部分分就没意思。

    这个式子的瓶颈在于 (n space ext{mod} space i) 的展开问题。所以只需要用 (n space ext{mod} space i = n - lfloor frac{n}{i} floor imes i) ,然后灵活用多项式的拆开与合并,一波整除分块带走即可。

    首先说好,这一次的推式子没有 莫比乌斯反演,也没有 奇怪的筛法,有的只是 多项式的灵活展开 与 整除分块

    于是我们开始推式子。

    [sum_{i=1}^n sum_{j=1}^m (n space ext{mod} space i) imes (m space ext{mod} space j) , i ot = j ]

    [= sum_{i=1}^n sum_{j=1}^m (n - lfloor frac{n}{i} floor imes i) imes (m - lfloor frac{m}{j} floor imes j) , i ot = j ]

    [= sum_{i=1}^n (n - lfloor frac{n}{i} floor imes i) imes sum_{j=1}^m (m - lfloor frac{m}{j} floor imes j) , i ot = j ]

    首先我们把 (i=j) 的答案丢掉,先做所有的答案。

    (f_n = sum_{i=1}^n (n - lfloor frac{n}{i} floor imes i)),则 ( ext{ans} = f_n imes f_m),考虑如何快速求 (f).

    [f_n = sum_{i=1}^n (n - lfloor frac{n}{i} floor imes i) ]

    [= n^2 - sum_{i=1}^n Big( lfloor frac{n}{i} floor imes i Big) ]

    然后你会发现这东西直接 整除分块(mathcal{O}( sqrt{n})) 很稳。

    最后多余的 (i=j) 的答案应该会是:

    [sum_{i=1}^{min(n,m)} (n space ext{mod} space i) imes (m space ext{mod} space i) ]

    [= sum_{i=1}^{min(n,m)} (n - lfloor frac{n}{i} floor imes i) imes (m - lfloor frac{m}{i} floor imes i) ]

    [= sum_{i=1}^{min(n,m)} Big( nm - ni lfloor frac{m}{i} floor - mi lfloor frac{n}{i} floor + i^2 lfloor frac{n}{i} floor lfloor frac{m}{i} floor Big) ]

    [= nm cdot min(n,m) - n imes sum_{i=1}^{min(n,m)} i lfloor frac{m}{i} floor - m imes sum_{i=1}^{min(n,m)} i lfloor frac{n}{i} floor + sum_{i=1}^{min(n,m)} i^2 lfloor frac{n}{i} floor lfloor frac{m}{i} floor ]

    (g_{n,k} = sum_{i=1}^k i lfloor frac{n}{i} floor)

    则:

    [= nm cdot min(n,m) - n imes g_{m,min(n,m)} - m imes g_{n,min(n,m)} + sum_{i=1}^{min(n,m)} i^2 lfloor frac{n}{i} floor lfloor frac{m}{i} floor ]

    显然,(g) 可以整除分块,所以整个式子都可以整除分块。

    对于最后的一块,我们令 (h_{n,m,k} = sum_{i=1}^{k} i^2 lfloor frac{n}{i} floor lfloor frac{m}{i} floor),但是注意到:

    (sum_{i=1}^{min(n,m)} i^2 lfloor frac{n}{i} floor lfloor frac{m}{i} floor) 的计算需要用到公式:

    [sum_{i=1}^n i^2 = frac{n (n+1) (2n+1)}{6} ]

    但是模意义下的除法不好做。这里有三种解决方法:

    • 求出模意义下 (6) 的逆元,可惜模数不是质数,我们只能用 ( ext{exgcd}) 去做。
    • 由于 (n(n+1)) 不会溢出,可以考虑 (n(n+1)/2 * (2n+1)/3),但是最终的结果会爆 ( ext{long long}),因此这种方法不行。
    • 直接开 ( ext{\_\_int128}) 解决所有问题。

    当然本人为了方便直接开了 ( ext{\_\_int128}),解决了所有的溢出计算问题。反正 ((10^9)^3 = 10^{27}) 肯定不会超过 (2^{127}),因为 (2^{127}) 大概有 (40) 位。
    上述方法已经说明,(f)(g) 均可以在 (mathcal{O}(sqrt{n} + sqrt{m} + sqrt{min(n,m)})) 的时间内解决。(n)(m)(min(n,m)) 均同级,所以最终时间复杂度为 (mathcal{O}(sqrt{n})).

    时间复杂度:(mathcal{O}(sqrt n)).

    实际得分:(100pts).

    这个故事告诉我们,清华集训的题并不难,在自己擅长的领域完全可以吊打集训队。(当然离那一天还很遥远)

    #pragma GCC optimize(2)
    #include<bits/stdc++.h>
    using namespace std;
    
    typedef __int128 ll;
    const ll MOD=19940417;
    
    inline ll read(){char ch=getchar(); int f=1; while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
    	ll x=0; while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x*f;}
    
    inline void write(ll x) {
    	if(x<0) {putchar('-');write(-x);return;}
    	if(x<10) {putchar(char(x%10+'0'));return;}
    	write(x/10);putchar(char(x%10+'0'));
    } //int128 需要快读快输
    inline ll sum(ll n) {return n*(n+1)/2%MOD;} //一维和
    inline ll pf(ll n) {return n*(n+1)*(2*n+1)/6%MOD;} //平方和
    inline ll min(ll n,ll m) {return n<m?n:m;} //手动最小值
    inline ll f(ll n) {
    	ll ans=0;
    	for(ll l=1,r;l<=n;l=r+1) {
    		r=n/(n/l);
    		ans=(ans+(n/l)*(sum(r)-sum(l-1))%MOD)%MOD;
    	} //printf("%lld
    ",ans);
    	return (n*n-ans)%MOD;
    } //整除分块计算 f
    
    inline ll g(ll n,ll k) {
    	ll ans=0;
    	for(ll l=1,r;l<=k;l=r+1) {
    		r=n/(n/l); r=min(r,k); //保证块不超过 k
    		ans=(ans+(n/l)*(sum(r)-sum(l-1))%MOD)%MOD;
    	} return ans;
    } //整除分块计算 g
    
    inline ll h(ll n,ll m,ll k) {
    	ll ans=0;
    	for(ll l=1,r;l<=k;l=r+1) {
    		r=min(n/(n/l),m/(m/l)); r=min(r,k); //保证块不超过 k
    		ans=(ans+(n/l)*(m/l)%MOD*(pf(r)-pf(l-1))%MOD)%MOD;
    	} return ans;
    } //整除分块计算 h
    
    int main() {
    	ll n=read(),m=read(),x=min(n,m);
    	ll tot1=f(n)*f(m)%MOD; //原本答案
    	ll tot2=(n*m*min(n,m)-n*g(m,x)-m*g(n,x)+h(n,m,x)+MOD+MOD)%MOD; //多余答案
    //	write(tot1),putchar(' '),write(tot2),putchar('
    ');
    	write((tot1-tot2+MOD)%MOD); //减法需要处理负数
    	return 0;
    }
    
    
    
  • 相关阅读:
    Python 以指定列宽格式化字符串
    Windows环境下QWT安装及配置
    iOS用户体验之-modal上下文
    android-调用系统的ContentPrivder获取单张图片实现剪切做头像及源代码下载
    Codeforces Round #253 (Div. 1) A Borya and Hanabi
    剑指offer 面试题9
    Memcache应用场景介绍
    [Unity-22] Coroutine协程浅析
    ZOJ 2706 Thermal Death of the Universe (线段树)
    为什么不建议用Table布局
  • 原文地址:https://www.cnblogs.com/bifanwen/p/13195919.html
Copyright © 2011-2022 走看看