Loj #3069. 「2019 集训队互测 Day 1」整点计数(min_25筛,圆上整点数,高斯质数 + 高斯整数)

在这里插入图片描述

题意:设 f ( x ) f(x) 表示圆心在(0,0) ,半径为 x x 的圆上整点个数,求 i = 1 n f ( i ) k \displaystyle\sum_{i = 1}^nf(i)^k

求圆上整点个数:半径固定情况下求圆上整点个数

参考视频:大佬讲解 一看就会
参考题解:大佬题解
把系数 4 提出来: a n s = 4 k i = 1 n h ( i ) k \displaystyle ans = 4^k\sum_{i = 1}^nh(i)^k

因为质因子的贡献可以单独计算, h ( i ) h(i) 是一个积性函数,25筛时根据提取的最小质因子是 4 n + 1 4n + 1 类型还是 4 n + 3 4n + 3 类型 乘上这种质因子的贡献。
当最小质因子是 4 n + 1 4n + 1 类型时: a n s = a n s + ( 2 e + 1 ) k ( s u m ( n p e + e 1 ) ) ans = ans + (2e + 1)^k*(sum(\frac{n}{p^e} + e\neq1))
当最小质因子是 4 n + 3 4n + 3 类型时: a n s = a n s + s u m ( n p e ) + e 1 ans = ans + sum(\frac{n}{p^e}) + e\neq 1

每一步素数的贡献:
如果该素数是 4 n + 1 4n + 1 类型, a n s + 3 k ans + 3^k
如果该素数是 4 n + 3 4n + 3 类型 或者该素数为 2, a n s + 1 ans + 1

由此需要筛出 4 n + 1 4n + 1 的素数个数以及 4 n + 3 4n + 3 的素数个数
g 1 ( n , j ) g1(n,j) 表示 :n 以内,i 是素数或 i 的最小素因子大于 p j p_j ,i 是 4 n + 1 4n + 1 类型的数
g 3 ( n , j ) g3(n,j) 表示 :n 以内,i 是素数或 i 的最小素因子大于 p j p_j ,i 是 4 n + 3 4n + 3 类型的数

g 1 ( n , 0 ) = n 1 4 g 3 ( n , 0 ) = n + 1 4 g1(n,0) = \frac{n - 1}{4},g3(n,0) = \frac{n + 1}{4}

s u m 1 ( p ) sum1(p) 表示 p 以内 4 n + 1 4n + 1 的质数个数, s u m 3 ( p ) sum3(p) 表示 p 以内 4 n + 3 4n + 3 的质数个数
当提取的最小质因子 p p 4 n + 1 4n + 1 类型时:
g 1 ( n ) = g 1 ( n p ) s u m 1 ( p ) g1(n) = g1(\frac{n}{p}) - sum1(p)
g 3 ( n ) = g 3 ( n p ) s u m 3 ( p ) g3(n) = g3(\frac{n}{p}) - sum3(p)
当提取的最小质因子 p p 4 n + 3 4n + 3 类型时:
g 1 ( n ) = g 3 ( n p ) s u m 3 ( p ) g1(n) = g3(\frac{n}{p}) - sum3(p)
g 3 ( n ) = g 1 ( n p ) s u m 1 ( p ) g3(n) = g1(\frac{n}{p}) - sum1(p)
对 g1而言:因为提取的质因子模4余3,整个数要模4余1,所以剩余的部分也是模4余3( 3 3 % 4 = 1 3 * 3 \% 4 = 1
对 g3 同理

再次证明,25筛无论是 g(n,j) 还是 s(n,j) 都可以筛一些非积性的东西,本质上是dp,只要考虑好贡献的转移就可以筛,也因此25筛更加灵活,并不是一套板子就能解决的事情

由于数据量达到了 1 0 11 10^{11} ,一不小心就会T掉,需要优化一些东西
对于贡献部分的 k k 次幂,指数 e e 的值不会太大(不会超过 200),小范围预处理部分数值的 k 次幂,复杂度为 n 3 4 log n \frac{n^{\frac{3}{4}}}{\log n}

l o n g l o n g long long 运算改为 i n t int 运算,利用 a d d , s u b , m u l add,sub,mul 函数减少取摸的次数以降低常数

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e6 + 10;
int num,tot;
bool ispri[maxn];
int pri[maxn],sum1[maxn],sum2[maxn];
ll w[maxn];
int g1[maxn],g2[maxn],id1[maxn],id2[maxn],sqr,val[maxn];
ll n,k;
int mod;
inline int add(int x, int y) {
  	x += y;
  	if (x >= mod) {
    	x -= mod;
  	}
  	return x;
}

inline int sub(int x, int y) {
  	x -= y;
  	if (x < 0) {
    	x += mod;
  	}
  	return x;
}

inline int mul(int x, int y) {
  return (long long) x * y % mod;
}
inline void sieve(int n) {
	ispri[0] = ispri[1] = true;
	num = 0;
	for (int i = 2; i <= n; i++) {
		if (!ispri[i]) {
			pri[++num] = i;
			sum1[num] = sum1[num - 1] + (i % 4 == 1);
			sum2[num] = sum2[num - 1] + (i % 4 == 3);
		}
		for (int j = 1; j <= num && i * pri[j] <= n; j++) {
			ispri[i * pri[j]] = true;
			if (i % pri[j] == 0) break;
		}
	}
}
inline int fpow(int a,ll b) {
	int r = 1;
	while (b) {
		if (b & 1) r = mul(r,a);
		b >>= 1;
		a = mul(a,a); 
	}
	return r;
}
inline int S(ll x,int y) {
	if (x <= 1 || pri[y] >= x) return 0;
	int t = x <= sqr ? id1[x] : id2[n / x];
	int ans = mul(sub(g1[t],sum1[y]),val[3]);					//(4n + 1)质数的贡献 
	ans = add(ans,sub(g2[t],sum2[y]));							//(4n + 3)质数的贡献 
	if (y == 0) ans++;											//特判 2 的贡献 
	for (int i = y + 1; i <= num && 1ll * pri[i] * pri[i] <= x; i++) {
		for (ll e = 1, pe = pri[i]; pe <= x; e++, pe = pe * pri[i]) {
			if (pri[i] % 4 == 1) {
				ans = add(ans,mul(val[2 * e + 1],add(S(x / pe,i),(e != 1))));
			} else {
				ans = add(ans,add(S(x / pe,i),(e != 1)));		//2和高斯质数的贡献 
			}
		}
	}
	return ans;
}
int main() {
	scanf("%lld%lld%d",&n,&k,&mod);
	sieve(maxn - 10);
	sqr = sqrt(n);
	for (int i = 1; i <= 500; i++)
		val[i] = fpow(i,k);
	for (ll i = 1,j; i <= n; i = j + 1) {
		j = n / (n / i);
		w[++tot] = n / i;
		ll p = n / i;
		g1[tot] = (p - 1) / 4 % mod;
		g2[tot] =  (p + 1) / 4 % mod;
		if (p <= sqr) id1[p] = tot;
		else id2[j] = tot;
	}
	for (int i = 1; i <= num; i++) {
		for (int j = 1; j <= tot && 1ll * pri[i] * pri[i] <= w[j]; j++) {
			ll t = w[j] / pri[i];
			int k = t <= sqr ? id1[t] : id2[n / t];
			if (pri[i] % 4 == 1) {
				g1[j] -= sub(g1[k],sum1[i - 1]);
				g2[j] -= sub(g2[k],sum2[i - 1]);
			} else if (pri[i] % 4 == 3) {
				g1[j] -= sub(g2[k],sum2[i - 1]);
				g2[j] -= sub(g1[k],sum1[i - 1]);
			}
			if (g1[j] < 0) g1[j] += mod;
			if (g2[j] < 0) g2[j] += mod;
		}
	}
	printf ("%d\n",mul(add(S(n,0),1),fpow(4,k)));
	return 0;
} 
发布了332 篇原创文章 · 获赞 10 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_41997978/article/details/104214700