【NOI P模拟赛】华莱士CNHLS(容斥,数论分块)

题意

出题人吃华 莱 士拉肚子了,心情不好,于是出了一道题面简单的难题。

T T T 组数据,对正整数 n n n
F ( n ) = ∑ i = 1 n μ 2 ( i ) i F(n)=\sum_{i=1}^n \mu^2(i)i F(n)=i=1nμ2(i)i

2 64 2^{64} 264 取模的结果。

n ≤ 1 0 14 , T ≤ 100. n\leq 10^{14},T\leq100. n1014,T100.

题解

莫比乌斯函数的平方,说明我们求的是 1 ∼ n 1\sim n 1n 中所有无多个完全平方数因子的数的和。

我们用总和减去存在多个完全平方数因子的数,令 s u m ( x ) = x ( x + 1 ) 2 sum(x)=\frac{x(x+1)}{2} sum(x)=2x(x+1),枚举最大完全平方数因子,可得
F ( n ) = s u m ( n ) − ∑ j = 2 n j 2 F ( ⌊ n j 2 ⌋ ) F(n)=sum(n)-\sum_{j=2}^{\sqrt n}j^2F(\lfloor\frac{n}{j^2}\rfloor) F(n)=sum(n)j=2n j2F(j2n)

于是,我们得到一个同 M i n 25 Min25 Min25 筛复杂度的做法,但是过不了。

考虑到枚举最大完全平方数因子太苛刻了,我们就直接枚举完全平方数因子,然后容斥。

具体地,对于每个质数,减去该质数平方的所有倍数,在对于每两个质数,加上平方之积的所有倍数……

这其实等价于对于每个数 i i i ,加上 μ ( i ) \mu(i) μ(i) 乘该数平方的所有倍数,于是可得
F ( n ) = ∑ j = 1 n μ ( j ) j 2 s u m ( ⌊ n j 2 ⌋ ) F(n)=\sum_{j=1}^{\sqrt n}\mu(j)j^2sum(\lfloor\frac{n}{j^2}\rfloor) F(n)=j=1n μ(j)j2sum(j2n)

这已经是十分优秀的 O ( T n ) O(T\sqrt n) O(Tn ) 复杂度了,但是还是过不了。

我们发现对于许多段连续的 j j j n j 2 \frac{n}{j^2} j2n 的值是相等的,而且,对于 j ≤ n 1 3 j\leq n^{\frac{1}{3}} jn31 ⌊ n j 2 ⌋ \lfloor\frac{n}{j^2}\rfloor j2n 只有 n 1 3 n^{\frac{1}{3}} n31 种取值,对于 j > n 1 3 j>n^{\frac{1}{3}} j>n31 ⌊ n j 2 ⌋ < n 1 3 \lfloor\frac{n}{j^2}\rfloor<n^{\frac{1}{3}} j2n<n31,因为下取整,同样只有 n 1 3 n^{\frac{1}{3}} n31 种取值。

所以我们数论分块,时间复杂度是 O ( T n 1 3 ) O(Tn^{\frac{1}{3}}) O(Tn31)

CODE

#include<set>
#include<map>
#include<cmath>
#include<stack>
#include<random>
#include<vector>
#include<bitset>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 10000005
#define LL long long
#define ULL unsigned long long
#define DB double
#define lowbit(x) (-(x) & (x))
#define ENDL putchar('\n')
#define FI first
#define SE second
LL read() {
    LL f=1,x=0;int s = getchar(); 
    while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
    while(s >= '0' && s <= '9') {x = (x<<3) + (x<<1) + (s^48); s = getchar();}
    return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar('0'+(x%10));}
void putnum(LL x) {
    if(!x) {putchar('0');return ;}
    if(x<0) {putchar('-');x = -x;}
    return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}

int n,m,s,o,k;
const LL MX = 10000000;
ULL f[MAXN],mu[MAXN],su[MAXN];
int p[MAXN/2],cnt;
bool v[MAXN];
void sieve(int n) {
	f[1] = 1;mu[1] = 1;
	for(int i = 2;i <= n;i ++) {
		if(!v[i]) p[++ cnt] = i,f[i] = i,mu[i] = 0ull-1ull;
		for(int j = 1;j <= cnt && i*p[j] <= n;j ++) {
			v[i*p[j]] = 1;
			if(i % p[j] == 0) {f[i*p[j]] = 0;break;}
			f[i*p[j]] = f[i]*p[j];
			mu[i*p[j]] = 0ull-mu[i];
		}
	}
	for(int i = 2;i <= n;i ++) f[i] += f[i-1];
	for(int i = 1;i <= n;i ++) su[i] = su[i-1] + mu[i]*i*i;
	return ;
}
ULL sm(LL x) {return (x&1) ? (((x+1)>>1) * x) : ((x>>1) * (x+1));}
ULL F(LL x) {
	if(x < 1) return 0;
	if(x <= MX) return f[x];
	ULL rs = 0;
	for(LL i = 1;i*1ll*i <= x;i ++) {
		LL r = sqrt((DB)x/(x/(i*1ll*i)));
		rs += sm(x/(i*1ll*i)) * (su[r] - su[i-1]);
		i = r;
	}
	return rs;
}
int main() {
	freopen("kfc.in","r",stdin);
	freopen("kfc.out","w",stdout);
	sieve(MX);
	int T = read();
	while(T --) {
		LL N = read();
		printf("%llu\n",F(N));
	}
	return 0;
}
posted @ 2021-11-01 16:05  DD_XYX  阅读(64)  评论(0编辑  收藏  举报