6785. 2020.08.07【NOI2020】模拟T3 重映射

题目

g ( x ) g(x) g(x) x x x的可重质因子数目, f ( x ) = 2 g ( x ) f(x)=2^{g(x)} f(x)=2g(x)

∑ i = 1 n f ( i ) \sum_{i=1}^nf(i) i=1nf(i)


正解

奇怪的数论知识增加了。

介绍一个叫power number的东西。power number是所有的质因子的指数都大于等于 2 2 2的数。

每个power number都可以表示成 a 2 b 3 a^2b^3 a2b3的形式,其中 a , b ∈ Z + a,b\in Z^+ a,bZ+

power number的数量是比较少的,所以有没有什么办法将值挂在power number上优化求值呢?

构造数论函数 u u u满足 u ∗ I ∗ I = f u*I*I=f uII=f,即 u = f ∗ μ ∗ μ u=f*\mu*\mu u=fμμ。容易得知 u u u也是个积性函数,并且手玩可以玩出 u ( p k ) u(p^k) u(pk) p p p为质数)的表达式。然后就可以发现 u u u只有在power number处有值。

接下来推一下式子: ∑ i = 1 n f ( i ) = ∑ i = 1 n ∑ j u ( j ) ∑ a , b [ j a b = i ] = ∑ j u ( j ) ∑ a , b [ j a b ≤ n ] = ∑ j u ( j ) ∑ a , b [ a b ≤ n j ] \sum_{i=1}^nf(i)=\sum_{i=1}^n\sum_j u(j)\sum_{a,b} [jab=i]=\sum_j u(j)\sum_{a,b} [jab\le n]=\sum_j u(j)\sum_{a,b} [ab\le \frac{n}{j}] i=1nf(i)=i=1nju(j)a,b[jab=i]=ju(j)a,b[jabn]=ju(j)a,b[abjn]

d ( n ) = ∑ a , b [ a b ≤ n ] d(n)=\sum_{a,b}[ab\le n] d(n)=a,b[abn]

这是一个经典问题。朴素求法是变成 ∑ i ⌊ n i ⌋ \sum_i \lfloor \frac{n}{i}\rfloor iin,整除分块。

然后发现时间卡不过去……

有个小优化:画出函数图像 x y = n xy=n xy=n,我们要求函数图像和 x x x正半轴和 y y y正半轴之间的整点个数。这个东西可以看成一个正方形和两个斜边是弯的三角形。正方形的整点可以 O ( 1 ) O(1) O(1)算,然后求弯的三角形内的整点就可以直接枚举每一行,这里行数是 O ( n ) O(\sqrt n) O(n )的。

形式化地说,就是 2 ∑ i = 1 ⌊ n ⌋ ⌊ n i ⌋ + ⌊ n ⌋ 2 2\sum_{i=1}^{\lfloor \sqrt n \rfloor} \lfloor \frac{n}{i} \rfloor+\lfloor \sqrt n \rfloor ^2 2i=1n in+n 2

左边的这个东西直接暴力算(不要整除分块!!!)。

(当然还有个更厉害的做法是在SB树上二分来拟合凸包,时间复杂度理论上可以达到 O ( n 1 3 lg ⁡ n ) O(n^{\frac{1}{3}}\lg n) O(n31lgn)


代码

using namespace std;
#include 
#include 
#include 
#include 
#include 
#define N 10000000
#define ll long long
ll n,sq,mo;
int p[N+1],np;
bool inp[N+1];
void init(int n){
	for (int i=2;i<=n;++i){
		if (!inp[i])
			p[++np]=i;
		for (int j=1;j<=np && i*p[j]<=n;++j){
			inp[i*p[j]]=1;
			if (i%p[j]==0)
				break;
		}
	}
}
ll calc(int k){return (1ll<=(ll)p[x]*p[x];++x){	
		ll _k=k;
		k/=(ll)p[x]*p[x];
		for (int i=2;k;k/=p[x],++i)
			dfs(x+1,k,uk*calc(i)%mo);
		k=_k;
	}
}
int main(){
	freopen("remapping.in","r",stdin);
	freopen("remapping.out","w",stdout);
	scanf("%lld%d",&n,&mo);
	sq=sqrt(n);
	for (;(sq+1)*(sq+1)<=n;++sq);
	init(sq);
	memset(d1,255,sizeof(int)*(sq+1));
	memset(d2,255,sizeof(int)*(sq+1));
	dfs(1,n,1);
	printf("%lld\n",ans);
	return 0;
}

你可能感兴趣的:(数学)