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; }
T2:
2 简单题1
2.1 Description
珈百璃刚刚学习了杜教筛,想做一道简单题练练手。给出N,求:
$\sum_{i = 1}^{N}i\mu (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) = n\mu(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} \right \rfloor)}{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\n", dfs(n)); return 0; }
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。
解析:
先来一发标准题解:
首先是一个公式:
$\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} \right \rfloor\left \lfloor \frac{M}{j} \right \rfloor$
把 [gcd = 1] 拿去反演得到:
$\sum_{d = 1}^{min(N, M)}\mu(d)\sum_{i = 1}^{\frac{N}{d}}\left \lfloor \frac{N}{di} \right \rfloor \sum_{j = 1}^{\frac{M}{d}}\left \lfloor \frac{M}{dj} \right \rfloor$
后面的部分可以预处理出来,那么每次询问都是 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} \right \rfloor$, 则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\n", ans); } return 0; }
OwenOwl Orz %%%