zoukankan      html  css  js  c++  java
  • [bzoj5332] [SDOI2018]旧试题

    题目描述

    时光匆匆,转眼间又是一年省选季……

    这是小 Q 同学第二次参加省队选拔赛。今年,小 Q 痛定思痛,不再冒险偷取试题,而是通过练习旧试题提升个人实力。可是旧试题太多了,小 Q 没日没夜地做题,却看不到前方的光明在哪里。

    一天,因做题过度而疲惫入睡的小 Q 梦到自己在考场上遇到了一道好像做过的题目,却怎么也想不起曾经自己是怎么解决它的,直到醒来还心有余悸。

    小 Q 眉头一皱,感觉事情不妙,于是他找到了你,希望你能教他解决这道题目。小 Q 依稀记得题目要计算如下表达式的值。

    ((sum_{i=1}^{A}sum_{j=1}^{B}sum_{k=1}^{C}d(ijk))mod(10^9+7))

    其中 d(ijk) 表示 i × j × k 的约数个数。

    输入输出格式

    输入格式:

    第一行包含一个正整数 T,表示有 T 组测试数据。

    接下来 T 行,每行描述一组测试数据,包含三个整数 A,B 和 C,含义见题目描述。

    输出格式:

    对于每组测试数据,输出一行,包含一个整数,表示所求表达式的值。

    输入输出样例

    输入样例:

    5
    10 10 10
    100 100 100
    1000 1000 1000
    10000 10000 10000
    100000 100000 100000
    

    输出样例:

    11536
    51103588
    165949340
    19234764
    176764584
    

    Solution

    关于约数个数(d(x))有这样一个性质:

    [d(ijk)=sum_{r|i}sum_{s|j}sum_{t|k}[(r,s)=1][(s,t)=1][(r,t)=1] ]

    证明:对于每一个质因子进行考虑,设(i,j,k)当前质因子的指数分别是(a,b,c),显然这个质因子的贡献是((a+b+c+1)),那么由于限制了(r,s,t)两两互质,最多这三者只有一个含有当前质因子:

    1. (r)含有(x)个,则表示当前质因子用了(x)
    2. (s)含有(x)个,则表示用了(a+x)
    3. (t)含有(x)个,则表示用了(a+b+x)
    4. 若都不含当前质因子,则表示没用这个质因子。

    所以总情况是(a+b+c+1)个,这样正好就可以算对。

    根据上面的证明,推广到(n)个数相乘也是成立的,只要枚举的数两两互质就好。

    所以带进原式可得:

    [ans=sum_{i=1}^asum_{j=1}^bsum_{k=1}^csum_{r|i}sum_{s|j}sum_{t|k}epsilon(r,s)epsilon(s,t)epsilon(r,t) ]

    这里将([gcd(i,j)=1])简记成(epsilon(i,j))

    然后把(r,s,t)提前:

    [ans=sum_{r=1}^{a}sum_{s=1}^bsum_{t=1}^clfloorfrac{a}{r} floorlfloorfrac{b}{s} floorlfloorfrac{c}{t} floorepsilon(r,s)epsilon(s,t)epsilon(r,t) ]

    然后对于每一个(epsilon(i,j))都进行莫比乌斯反演,整理化简可得:

    [ans=sum_{u=1}^{min(a,b)}sum_{v=1}^{min(b,c)}sum_{w=1}^{min(a,c)}mu(u)mu(v)mu(w)s(frac{a}{{ m{lcm}}(u,v)})s(frac{b}{{ m{lcm}}(v,w)})s(frac{c}{{ m{lcm}}(u,w)}) ]

    其中:

    [s(n)=sum_{i=1}^{n}lfloorfrac{n}{i} floor=sum_{i=1}^{n}d(i) ]

    这一步由约数个数的定义可得。

    然后,,上面那个长的像(O(n^3))的东西就是正解,,

    考虑下如何让式子后面那一块有值,当且仅当:

    [mu(u) e 0,mu(v) e 0,mu(w) e 0,{{ m{lcm}}(u,v)}leqslant a,{{ m{lcm}}(v,w)}leqslant b,{{ m{lcm}}(u,w)}leqslant c ]

    所以对于每一个质数,在(u,v,w)的质因数分解中只可能至多出现一次,所以我们爆搜有没有出现的八种情况,然后剪枝。。

    • 可以预处理出前面一部分的答案,当当前质数大于限制的时候,且范围较小,可以直接用预处理出来的答案。
    • 考虑到超过两个含有当前质数时,除开当前的(mu)的影响,所有情况答案都是一样的,所以可以少算三种情况。
    • 可以证明从大到小枚举质数,上面那个剪枝会快些。。。

    然后尽量不要取模,(ans)不会爆(long\,\,long),最后取一次模就行了。

    代码很好写(是真的比三元环计数好些啊,3k的码量差距啊)

    #include<bits/stdc++.h>
    using namespace std;
     
    void read(int &x) {
        x=0;int f=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
        for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
    }
      
    void print(int x) {
        if(x<0) putchar('-'),x=-x;
        if(!x) return ;print(x/10),putchar(x%10+48);
    }
    void write(int x) {if(!x) putchar('0');else print(x);putchar('
    ');}
     
    const int maxn = 1e5+10;
    const int lim = 40;
    const int mod = 1e9+7;
     
    #define ll long long 
     
    int f[maxn],pri[maxn],tot,d[maxn],ff[lim+1][lim+1][lim+1],vis[maxn];
     
    void prepare() {
        int n=1e5;for(int i=1;i<=n;i++) d[i]=1;
        for(int i=2;i<=n;i++) {
            if(!vis[i]) pri[++tot]=i;
            for(int j=i;j<=n;j+=i) d[j]++,vis[j]=1;
        }
        for(int i=1;i<=n;i++) f[i]=(f[i-1]+d[i])%mod;
        for(int i=1;i<=lim;i++)
            for(int j=1;j<=lim;j++)
                for(int k=1;k<=lim;k++)
                    ff[i][j][k]=(0ll+ff[i-1][j][k]+ff[i][j-1][k]+ff[i][j][k-1]-ff[i-1][j-1][k]-ff[i-1][j][k-1]-ff[i][j-1][k-1]+ff[i-1][j-1][k-1]+d[i*j*k])%mod;
    }
     
    ll dfs(int now,int a,int b,int c) {
        if(!now) return 1ll*f[a]*f[b]%mod*f[c]%mod;
        if((!a)&&(!b)&&(!c)) return 0;
        int p=pri[now],mx=max(max(a,b),c);
        if(p>mx&&mx<=lim) return ff[a][b][c];
        ll ans=dfs(now-1,a,b,c);
        if(a>=p&&b>=p) ans-=dfs(now-1,a/p,b/p,c);
        if(a>=p&&c>=p) ans-=dfs(now-1,a/p,b,c/p);
        if(b>=p&&c>=p) ans-=dfs(now-1,a,b/p,c/p);
        if(a>=p&&b>=p&&c>=p) ans+=dfs(now-1,a/p,b/p,c/p)<<1ll;
        return ans;
    }
     
    void solve() {
        int a,b,c;read(a),read(b),read(c);
        int mx=max(max(a,b),c);
        int now=tot;
        while(pri[now]>mx) now--;
        write((int)((dfs(now,a,b,c)%mod+mod)%mod));
    }
     
    signed main() {
        prepare();int t;read(t);
        while(t--) solve();
        return 0;
    }
    
  • 相关阅读:
    hadoop学习--搜索引擎面临的数据和计算难题
    解析excel
    sql批量入库
    tomcat-nginx配置
    友链
    Vue+ElementUI搭建一个后台管理框架
    OnePill本地保存用户的结构
    Android集成JPush(极光推送)
    Android三种风格的底部导航栏
    Android实现EditText插入表情、超链接等格式
  • 原文地址:https://www.cnblogs.com/hbyer/p/10233631.html
Copyright © 2011-2022 走看看