数
题目概述
题解
首先有
n
⩽
1
0
11
n\leqslant 10^{11}
n⩽1011的数据范围,大概是个筛子。
首先我们关注我们的判定条件:
k
=
c
2
−
a
2
−
b
2
(
b
+
c
=
a
∧
a
,
b
,
c
∈
N
+
)
k=c^2-a^2-b^2(b+c=a\wedge a,b,c\in N^{+})
k=c2−a2−b2(b+c=a∧a,b,c∈N+)。
化简一下,
k
=
(
b
+
d
)
2
−
(
b
−
d
)
2
−
b
2
=
4
b
d
−
b
2
=
b
(
4
d
−
b
)
(
b
∈
(
d
,
4
d
)
)
k=(b+d)^2-(b-d)^2-b^2\\ =4bd-b^2=b(4d-b)\,(b\in(d,4d))
k=(b+d)2−(b−d)2−b2=4bd−b2=b(4d−b)(b∈(d,4d))我们相当于可以将可以将我们的的
k
k
k拆成
x
⋅
y
x\cdot y
x⋅y,并且满足条件
y
∈
(
0
,
3
x
)
,
4
∣
(
x
+
y
)
y\in(0,3x),4|(x+y)
y∈(0,3x),4∣(x+y)。
显然如果我们直接筛这东西的话是
O
(
n
ln
n
)
O\left(n\ln n\right)
O(nlnn)的,但这个判定条件明显可以继续往下导出一些东西。
我们可以设
k
=
2
a
×
b
k=2^a\times b
k=2a×b,其中,
b
b
b是奇数。
我们考虑,当
a
=
0
a=0
a=0时,如果
b
b
b不是质数,那么我们必然可以将
b
b
b拆成大的因数与小的因数,大的因数的三倍肯定大于小的因数。
所以,只有当
k
k
k为大于
2
2
2的质数时并且
k
%
4
=
3
k\%4=3
k%4=3,只存在唯一一种的拆分方式
(
k
,
1
)
(k,1)
(k,1)满足条件。
而当
a
=
1
a=1
a=1或者
a
=
3
a=3
a=3,必然会导致
2
2
2分配的不均,不能使
4
∣
a
+
b
4|a+b
4∣a+b。
当
a
=
2
a=2
a=2时,我们必须将给
x
,
y
x,y
x,y分别一个
2
2
2,这时,就没必要考虑它们各自模
4
4
4是多少了。
但这种情况下还是有我们上面的可拆分问题,所以这里限制的是
k
k
k是某个大于
2
2
2的质数的
4
4
4倍或
4
4
4。
同样,我们
a
=
4
a=4
a=4的时候也只存在两边都给
2
2
2个
2
2
2的分配方式,同时需要保证
k
k
k是质数的
16
16
16倍或
16
16
16。
所以我们最后要求的是
n
n
n以内模
4
4
4余
3
3
3的质数,除
4
4
4是质数的数,除
16
16
16是质数的数以及
4
4
4和
16
16
16。
实际上就是模
4
4
4余
3
3
3的质数数量与模
4
4
4余
1
1
1的质数数量,这东西要算前缀和,不就是
min
_
25
\min\_25
min_25筛嘛。
我们可以记录下这两者的前缀和,显然,每次我们乘上一个质数我们可以根据它关于
4
4
4的余数得到它在
min
_
25
\min\_25
min_25中容斥的变化。很简单就可以得到只剩质数处取值的前缀和了。
由于我们本身就只要质数处,所以就没必要进行第二步了。由于
min
_
25
\min\_25
min_25求了每个
⌊
n
i
⌋
\lfloor\frac{n}{i}\rfloor
⌊in⌋处的前缀和,直接算答案就行。
时间复杂度 O ( n 3 4 ln n ) O\left(\frac{n^{\frac{3}{4}}}{\ln n}\right) O(lnnn43)。
源码
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> pii;
#define MAXN 500005
#define MAXM 1000005
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define reg register
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;}return t;}
int id1[MAXN],id2[MAXN],idx,prime[MAXN],cntp,n1,sum[MAXN][2];
LL n,a[MAXM];pii F[MAXM];
bool oula[MAXN];
void init(){
for(int i=2;i<=n1;i++){
if(!oula[i]){
prime[++cntp]=i;
sum[cntp][0]=sum[cntp-1][0]+(i%4==1);
sum[cntp][1]=sum[cntp-1][1]+(i%4==3);
}
for(int j=1;1ll*i*prime[j]<=n1&&j<=cntp;j++){
oula[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}
}
int getId(LL x){if(!x)return 0;return x<=n/x?id1[x]:id2[n/x];}
int main(){
//freopen("number.in","r",stdin);
//freopen("number.out","w",stdout);
read(n);n1=sqrt(n);init();
for(LL l=1,r;l<=n;l=r+1)r=n/(n/l),a[++idx]=n/l;
for(int i=1;i<=idx;i++)(a[i]<=n/a[i]?id1[a[i]]:id2[n/a[i]])=i;
for(int i=1;i<=idx;i++)F[i]=mkpr((a[i]-1)/4,(a[i]+1)/4);
for(int i=2;i<=cntp;i++){
LL tp=1ll*prime[i]*prime[i];
if(prime[i]%4==1)
for(int j=1;j<=idx&&a[j]>=tp;j++){
LL t=a[j]/prime[i];int x=getId(t);
F[j].fir-=F[x].fir-sum[i-1][0];
F[j].sec-=F[x].sec-sum[i-1][1];
}
else for(int j=1;j<=idx&&a[j]>=tp;j++){
LL t=a[j]/prime[i];int x=getId(t);
F[j].fir-=F[x].sec-sum[i-1][1];
F[j].sec-=F[x].fir-sum[i-1][0];
}
}
LL tmp1=F[getId(n)].sec;
LL tmp2=F[getId(n>>2)].fir+F[getId(n>>2)].sec+(n>=4);
LL tmp3=F[getId(n>>4)].fir+F[getId(n>>4)].sec+(n>=16);
printf("%lld\n",tmp1+tmp2+tmp3);
return 0;
}