zoukankan      html  css  js  c++  java
  • 7.30 Test——Math Theory 2

    T1:

    1 难题

    1.1 Description
    珈百璃正在看 ZJOI 2018 的一道题“线图”。
    对于无向图G = (V, E),它的线图L(G)也是一个无向图:
       它的点集大小为|E|,每个点唯一对应着原图的一条边。
       两个点之间有边当且仅当这两个点对应的边在原图上有公共点(注意不会有自环)。
    ZJOI 那道题要对一棵树G 求L(L(L(L(L(L(L(L(L(G)))))))))的点数,
    这对九条可怜很简单,但对珈百璃太难了,于是珈百璃打算先解决一个更简单的问题:
    对一棵树G,求L(L(G))的点数。

    1.2 Task
    1.2.1 Input
    第一行一个正整数n 表示G 的点数;
    接下来n - 1每行两个数表示G 的一条边。

    1.2.2 Output
    一行一个数表示答案。

    1.3 Sample
    1.3.1 Input
    5
    1 2
    2 3
    2 5
    3 4
    1.3.2 Output
    4

    1.4 Constraint
    对于30%的数据,n <= 100;
    对于100%的数据,n  <= 105

    1.5 Hint
    这张图描绘了样例中的G,L(G) 和 L(L(G))。

    解析:

      难题还真是难啊

      不难看出就是求L(G)中的边数, L(G)中的边, 由原图中有公共点的两条边变成点后连接而成, 因此考虑枚举原图中的交点, 与它相连的边在L(G)中变成点后就会两两相连,所以一个原图中的交点对答案产生的贡献为C(deg, 2), 其中deg是它的度数

     代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 100004;
    
    int n, deg[maxn];
    ll ans;
    
    int main()
    {
        freopen("a.in", "r", stdin);
        freopen("a.out", "w", stdout);
        scanf("%d", &n);
        for(int i = 1; i < n; ++i)
        {
            int x, y;
            scanf("%d%d", &x, &y);
            deg[x] ++;
            deg[y] ++;
        }
        for(int i = 1; i <= n; ++i)
            ans += 1LL * deg[i] * (deg[i] - 1) / 2;
        printf("%lld", ans);
        return 0;
    }
    a

    T2:

    2 简单题1

    2.1 Description
    珈百璃刚刚学习了杜教筛,想做一道简单题练练手。给出N,求:
    $sum_{i = 1}^{N}imu (i)$
    答案模109  + 7。

    2.2 Task
    2.2.1 Input
    一行一个正整数N。
    2.2.2 Output
    一行一个数表示答案。

    2.3 Sample
    2.3.1 Input
    15
    2.3.2 Output
    5

    2.4 Constraint
    对于30%的数据,N <= 106
    对于60%的数据,N <= 109
    对于100%的数据,N <= 1010

    解析:

      $f(n) = nmu(n)$ , $g(n) = n$

      由狄利克雷卷积得 $h(n) = sum_{d|n}g(frac{n}{d})f(d)$

      化简得$h(n) = [n = 1]$,

      设$S(n)$$f(n)$得前缀和, 则有:

      $S(n) = frac{sum_{i = 1}^{n}h(i) - sum_{i = 2}^{n}g(i)S(left lfloor frac{n}{i} ight floor)}{g(1)}$

      这样就可以用欧拉筛算出前6e6的答案, 再用记忆化搜索、整除分块递归求解

     代码:

    #include<cstdio>
    #include<map>
    using namespace std;
    typedef long long ll;
    const int maxn = 6000006;
    const int mod = 1000000007;
    
    int pri[800000], miu[maxn], cnt;
    ll n, s[maxn], inv;
    bool notp[maxn];
    
    ll qpow(ll x, int y)
    {
        ll ret = 1;
        while(y)
        {
            if(y&1)
                ret = ret * x % mod;
            x = x * x % mod;
            y>>=1;
        }
        return ret;
    }
    
    void Euler()
    {
        miu[1] = 1;
        s[1] = 1;
        for(int i = 2; i <= 6000000; ++i)
        {
            if(!notp[i])
            {
                miu[i] = -1;
                pri[++cnt] = i;
            }
            for(int j = 1; j <= cnt && i * pri[j] <= 6000000; ++j)
            {
                notp[i * pri[j]] = 1;
                if(i % pri[j] == 0)
                {
                    miu[i * pri[j]] = 0;
                    break;
                }
                miu[i * pri[j]] = -miu[i];
            }
            s[i] = (s[i-1] + 1LL * i * miu[i]) % mod;
            s[i] = (s[i] + mod) % mod;
        }
            
    }
    
    map<ll, ll> mp;
    
    ll dfs(ll x)
    {
        if(x <= 6000000)    return s[x];
        if(mp.find(x) != mp.end())    return mp[x];
        ll ret = 1;
        for(ll l = 2, r; l <= x; l = r + 1)
        {
            r = x / (x / l);
            ll a = 1LL * ((((l + r) % mod) * ((r - l + 1) % mod) % mod) * inv) % mod;
            ret = ret - (a * dfs(x/l) % mod);
            ret = (ret % mod + mod) % mod;
        }
        return mp[x] = ret;
    }
    
    int main()
    {
        freopen("b.in", "r", stdin);
        freopen("b.out", "w", stdout);
        inv = qpow((ll)2, mod - 2);
        Euler();
        scanf("%lld", &n);
        printf("%lld
    ", dfs(n));
        return 0;
    }
    b

    T3:

    3 简单题2

    3.1 Description
    珈百璃刚刚学习了莫比乌斯反演,想再做一道简单题练练手。
    $sigma_{0}(x)$为x 的约数个数,给出N 和M,求:
    $sum_{i=1}^{N}sum_{j=1}^{M}sigma_{0}(ij)$
    3.2 Task
    3.2.1 Input
    本题每个测试点有T 组数据,因此第一行一个数T。
    接下来每行两个数表示N;M。
    3.2.2 Output
    输出T 行答案。

    3.3 Sample
    3.3.1 Input
    2
    7 4
    5 6
    3.3.2 Output
    110
    121
    3.4 Constraint
    对于20%的数据,N, M <= 100;
    另有30%的数据,T <= 10,N, M <= 1000;
    另有20%的数据,T <= 10;
    对于100%的数据,T <= 50000,1 <= N, M <= 50000。

    原题:Bzoj3994:[SDOI2015]约数个数和

    解析:

      先来一发标准题解:

     

      首先是一个公式:  

     $sigma(ij) = sum_{x|i}sum_{y|j}[gcd(x, y) = 1]$

      因为对于一个质因子 p,假设 i,j 分别有 pa,pb,而 ij 的某个因子有 pc,如果 c ≤ a 则使 x 含 pc 否则使 y 含 pc−a,这样恰好建立了一一映射。带进去得到答案是 :

    $sum_{i = 1}^{N}sum_{j = 1}^{M}sum_{x|i}sum_{y|j}[gcd(x, y) = 1]$

      交换和号,先枚举 x,y:

    $sum_{i = 1}^{N}sum_{j = 1}^{M}[gcd(i, j) = 1]left lfloor frac{N}{i} ight floorleft lfloor frac{M}{j} ight floor$

      把 [gcd = 1] 拿去反演得到:

     $sum_{d = 1}^{min(N, M)}mu(d)sum_{i = 1}^{frac{N}{d}}left lfloor frac{N}{di} ight floor sum_{j = 1}^{frac{M}{d}}left lfloor frac{M}{dj} ight floor$

      后面的部分可以预处理出来,那么每次询问都是 O(N) 的了。

     

      题解大部分都说的比较详细, 只是有两处还有另外的解释:

      1.对于公式一,另一种理解为对于每一个质因子$P_{k}$, 设i = i_ * Pka, j = j_ * Pkb, gcd(x, y), 分别取(i_ * Pka, j_)、(i_ * Pka-1, j_)...(i_ , j_)...(i_, j_ * Pkb-1(i_, j_ * Pkb), 则共有(a + b + 1)组,正好符合Pk对i * j约数个数的贡献

      2.记$f(i) = sum_{j=1}^{i}left lfloor frac{i}{j} ight floor$, 则j对$f(i)$的贡献即是i中j的倍数的个数, 也就是1~i中约数中含j的个数,加起来就是约数个数的前缀和

     代码:

     

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 50006;
    
    int pri[maxn], d[maxn], miu[maxn], cnt, n, m, T;
    ll ans, tao[maxn];
    bool notp[maxn];
    
    void Euler()
    {
        miu[1] = 1;
        tao[1] = 1;
        for( int i = 2 ; i <= 50000 ; ++i)
        {
            if( !notp[i] )
            {
                pri[++cnt] = i ;
                tao[i] = 2 ;
                d[i] = 1 ;
                miu[i] = -1;
            }
            for( int j = 1 ; j <= cnt && i * pri[j] <= 50000 ; ++j)
            {
                notp[i * pri[j]] = 1;
                if( i % pri[j] == 0 )
                {
                    tao[ i*pri[j] ] = 1LL * tao[i] / ( d[i] + 1 ) * ( d[i] + 2 ) ;
                    d[ i*pri[j] ] = d[i] + 1 ;
                    miu[ i*pri[j] ] = 0 ;
                    break ;
                }
                tao[ i*pri[j] ] = 1LL * tao[i] * 2 ;
                d[ i*pri[j] ] = 1 ;
                miu[ i*pri[j] ] = -miu[i] ;
            }
            miu[i] += miu[i-1];
            tao[i] += tao[i-1];
        }
    }
    
    int main()
    {
        freopen("c.in", "r", stdin);
        freopen("c.out", "w", stdout);
        Euler();
        scanf("%d", &T);
        while(T--)
        {
            ans = 0;
            scanf("%d%d", &n, &m);
            if(n > m)    swap(n, m);
            for(int l = 1, r; l <= n; l = r + 1)
            {
                r = min(n / (n / l), m / (m / l));
                ans += (miu[r] - miu[l-1]) * tao[n/l] * tao[m/l];
            }
            printf("%lld
    ", ans);
        }
        return 0;
    }
    c

     

    OwenOwl Orz %%%

     

  • 相关阅读:
    第3章 MFC框架程序剖析
    第2章 掌握C++
    第1章 Windows程序内部运行机制
    【MFC】画线
    使用RegSetValueEx修改注册表时遇到的问题(转)
    读书笔记
    POJ 1182[并查集]
    读书笔记
    HihoCoder 1532 : 最美和弦
    HihoCode 1531 : 德国心脏病
  • 原文地址:https://www.cnblogs.com/Joker-Yza/p/11272465.html
Copyright © 2011-2022 走看看