• 《Math》


    https://ac.nowcoder.com/acm/contest/22769/C

    这题确实不错,很看转化。

    这里的下标其实没有那么重要,主要考的是反演。

    定义f[x] = gcd(i,j) = x的数量,F[x] = gcd(i,j) = k * x{x >= 1} 的数量,也就是x的倍数。

    那么很显然我们可以得到:$F[d] = sum_{d | t}^{t <= n} f(t)$

    由莫比乌斯反演式二:可得$f[d] = sum_{d | t}^{t <= n} mu (frac{t}{d})F(t)$

    代入d = 1得,$f[1] = sum_{t = 1}^{n} mu (t) * F(t)$

    这里我们只需要预处理出F函数即可算出f(1)。

    考虑一下F函数怎么算:

    对于i,倍数之间的组合,gcd肯定都是i的倍数,所以我们只需要迭代做一个类似前缀和的循环即可计算所有倍数组合的方案数。

    // Author: levil
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int,int> pii;
    const int N = 1e5 + 5;
    const int M = 1e5 + 5;
    const LL Mod = 1e9 + 7;
    #define dbg(ax) cout << "now this num is " << ax << endl;
    inline long long ADD(long long x,long long y) {return (x + y) % Mod;}
    inline long long MUL(long long x,long long y) {return x * y % Mod;}
    
    int a[N],b[N],prime[N],tot = 0,mu[N],cnt[N];
    LL F[N];
    bool vis[N];
    void init() {
        mu[1] = 1;
        for(int i = 2;i < N;++i) {
            if(!vis[i]) {
                prime[++tot] = i;
                mu[i] = -1;
            }
            for(int j = 1;j <= tot && prime[j] * i < N;++j) {
                vis[prime[j] * i] = 1;
                if(i % prime[j] == 0) break;
                else mu[i * prime[j]] = -mu[i];
            }
        }
    }
    void solve() { 
        int n;scanf("%d",&n);
        for(int i = 1;i <= n;++i) scanf("%d",&a[i]);
        for(int i = 1;i <= n;++i) scanf("%d",&b[i]);
        for(int i = 1;i <= n;++i) {
            for(int j = i;j <= n;j += i) cnt[a[b[j]]]++;
            for(int j = i;j <= n;j += i) F[i] += cnt[b[a[j]]];
            for(int j = i;j <= n;j += i) cnt[a[b[j]]]--;
        }
        LL ans = 0;
        for(int i = 1;i <= n;++i) {
            ans += mu[i] * F[i];
        }
        printf("%lld
    ",ans);
    }   
    int main() {
        init();
        solve();
        system("pause");
        return 0;
    }
    View Code

    https://ac.nowcoder.com/acm/contest/22769/F

    这题也很不错:

    对式子二化简一下就是$f[r][n] = sum_{d | n}^{}f[r - 1][d] = f[r][n] * I$

    考虑当r = 0时,对n质因子分解,质因子个数为m,显然每次对于一个质因子的k次,肯定要都在一边,那么构成的方案数就是左边选中x个质因子。

    也就是2 ^ m。

    此时满足f[0][x] = f[0][a] * f[0][b],即积性函数。

    由于积性函数相互卷积之后依然是积性函数。

    所以f[r]也是积性函数。

    所以$f[r][n] = sum_{i = 1}^{m} f[r - 1][(pi) ^ k]$

    我们只需要预处理出f[r][pi ^ k]即可。

    这里考虑dp[i][j] - r = i,次数 = j 因为不管你质因子是什么,这个贡献只和质数的次数有关。

    这里的dp其实是做了一个前缀和,因为关于k次的转移,就是0~k次的任意组合。

    // Author: levil
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int,int> pii;
    const int N = 1e6 + 5;
    const int M = 1e5 + 5;
    const LL Mod = 1e9 + 7;
    #define dbg(ax) cout << "now this num is " << ax << endl;
    inline long long ADD(long long x,long long y) {return (x + y) % Mod;}
    inline long long MUL(long long x,long long y) {return x * y % Mod;}
    
    bool vis[N];
    int prime[N],n,tot = 0;
    LL dp[N][30];
    void init() {
        for(int i = 2;i < N;++i) {
            if(!vis[i]) {
                prime[++tot] = i;
            }
            for(int j = 1;j <= tot && prime[j] * i < N;++j) {
                vis[prime[j] * i] = 1;
                if(i % prime[j] == 0) break;
            }
        }
        dp[0][0] = 1;
        for(int j = 1;j < 30;++j) dp[0][j] = 2;
        for(int i = 1;i < N;++i) {
            dp[i][0] = 1;
            for(int j = 1;j < 30;++j) {
                dp[i][j] = ADD(dp[i][j - 1],dp[i - 1][j]); 
            }   
        }
    }
    void solve() { 
        int q;scanf("%d",&q);
        while(q--) {
            int r,n;scanf("%d %d",&r,&n);
            LL ans = 1;
            for(int i = 1;i <= tot && prime[i] <= n;++i) {
                if(!vis[n]) break;
                if(n % prime[i] == 0) {
                    int k = 0;
                    while(n % prime[i] == 0) n /= prime[i],++k;
                    ans = MUL(ans,dp[r][k]);
                }
            }
            if(n != 1) ans = MUL(ans,dp[r][1]);
            printf("%lld
    ",ans);
        }
    }   
    int main() {
        init();
        solve();
       // system("pause");
        return 0;
    }
    View Code

     https://ac.nowcoder.com/acm/contest/22769/E

    这题还是比较好推的。

    最后推出来:$sum_{d = 1}^{k} d sum_{t = 1}^{[frac{k}{d}]}mu (t) * [frac{m}{t}] ^ n$

    后面那一堆显然可以整除分块预处理一下。

    这里一开始整除分块里用了ADD这些函数,然后就T了,调用次数多了常数还是有点大。

    // Author: levil
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int,int> pii;
    const int N = 1e5 + 5;
    const int M = 1e6 + 5;
    const LL Mod = 1e9 + 7;
    #define dbg(ax) cout << "now this num is " << ax << endl;
    inline long long ADD(long long x,long long y) {
        if(x + y < 0) return ((x + y) % Mod + Mod) % Mod;
        return (x + y) % Mod;
    }
    inline long long MUL(long long x,long long y) {
        if(x * y < 0) return ((x * y) % Mod + Mod) % Mod;
        return x * y % Mod;
    }
    inline long long DEC(long long x,long long y) {
        if(x - y < 0) return (x - y + Mod) % Mod;
        return (x - y) % Mod;
    }
    
    int prime[N],tot = 0,mu[N],n,k;
    bool vis[N];
    LL f[N],pw[N];
    LL quick_mi(LL a,LL b) {
        LL re = 1;
        while(b) {
            if(b & 1) re = re * a % Mod;
            a = a * a % Mod;
            b >>= 1;
        }
        return re;
    }
    void init() {
        mu[1] = 1;
        for(int i = 2;i < N;++i) {
            if(!vis[i]) {
                prime[++tot] = i;
                mu[i] = -1;
            }
            for(int j = 1;j <= tot && prime[j] * i < N;++j) {
                vis[prime[j] * i] = 1;
                if(i % prime[j] == 0) break;
                else mu[prime[j] * i] = -mu[i];
            }
        }
        for(int i = 1;i < N;++i) pw[i] = quick_mi(i,n);
        for(int i = 1;i < N;++i) {
            LL tmp = 0;
            mu[i] += mu[i - 1];
            for(int L = 1,r = 0;L <= i;L = r + 1) {
                r = i / (i / L);
                LL f = 1LL * (mu[r] - mu[L - 1]) * pw[i / L];
                if(f < 0) f += Mod;
                tmp = (tmp + f) % Mod;
            }
            f[i] = tmp;
        }
    }
    void solve() { 
        scanf("%d %d",&n,&k);
        init();
        LL ans = 0;
        for(int d = 1;d <= k;++d) {
            ans = ADD(ans,MUL(d,f[k / d]));
        }
        printf("%lld
    ",ans);
    }   
    int main() {
        solve();
       // system("pause");
        return 0;
    }
    View Code

    https://ac.nowcoder.com/acm/contest/22769/D

    这题挺难推的,首先考虑把有序对转化成无序对,然后答案就是 - $sum_{i = 1}^{n}a[i]$,然后再除以2.

    至于怎么反演可以参考:https://www.cnblogs.com/zwjzwj/p/15503939.html

    // Author: levil
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int,int> pii;
    const int N = 2e5 + 5;
    const int M = 1e6 + 5;
    const LL Mod = 998244353;
    #define dbg(ax) cout << "now this num is " << ax << endl;
    inline long long ADD(long long x,long long y) {return (x + y) % Mod;}
    inline long long MUL(long long x,long long y) {
        if(x * y < 0) return ((x * y) % Mod + Mod) % Mod;
        return x * y % Mod;
    }
    inline long long DEC(long long x,long long y) {return (x - y + Mod) % Mod;}
    
    int n,a[N],tot = 0,prime[M],mu[M],cnt[M];
    LL f[M];
    bool vis[M];
    void init() {
        mu[1] = 1;
        for(int i = 2;i < M;++i) {
            if(!vis[i]) {
                prime[++tot] = i;
                mu[i] = -1;
            }
            for(int j = 1;j <= tot && prime[j] * i < M;++j) {
                vis[prime[j] * i] = 1;
                if(i % prime[j] == 0) break;
                else mu[prime[j] * i] = -mu[i];
            }
        }
        for(int i = 1;i < M;++i) {
            for(int j = i;j < M;j += i) f[j] = ADD(f[j],i * mu[i]);
        }
    }
    LL quick_mi(LL a,LL b) {
        LL re = 1;
        while(b) {
            if(b & 1) re = re * a % Mod;
            a = a * a % Mod;
            b >>= 1;
        }
        return re;
    }
    void solve() { 
        scanf("%d",&n);
        int up = 0;
        LL sum = 0;
        for(int i = 1;i <= n;++i) scanf("%d",&a[i]),up = max(up,a[i]),cnt[a[i]]++,sum = ADD(sum,a[i]);
        LL ans = 0;
        for(int d = 1;d <= up;++d) {
            LL ma = 0;
            for(int i = d;i <= up;i += d) ma = ADD(ma,MUL(i / d,cnt[i]));
            ma = MUL(ma,ma);
            ma = MUL(ma,f[d]);
            ans = ADD(ans,MUL(d,ma));
        }
        ans = DEC(ans,sum);
        ans = MUL(ans,quick_mi(2,Mod - 2));
        printf("%lld
    ",ans);
    
    }   
    int main() {
        init();
        solve();
       // system("pause");
        return 0;
    }
    View Code
  • 相关阅读:
    冲刺计划安排
    团队作业7——Alpha冲刺之事后诸葛亮
    团队作业6--展示博客(Alpha版本)
    团队作业5——测试与发布(Alpha版本)
    团队项目汇总
    【Java】五种常见排序之------------------快速排序
    【Java】五种常见排序之----------------归并排序
    【Java】五种常见排序之----------------------插入排序
    【Java】五种常见排序之---------选择排序
    【Java】五种常见排序之-----------冒泡排序
  • 原文地址:https://www.cnblogs.com/zwjzwj/p/15500970.html
Copyright © 2020-2023  润新知