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 %%%

 

原文地址:https://www.cnblogs.com/Joker-Yza/p/11272465.html