文章目录
R e s u l t Result Result
H y p e r l i n k Hyperlink Hyperlink
D e s c r i p t i o n Description Description
一张无限大的网格,若从 ( x , y ) (x,y) (x,y)出发,每一步可以类似于马一样移动,长度分别为 a , b a,b a,b(对于经常提起的中国象棋中的马的话,则有 a = 1 , b = 2 a=1,b=2 a=1,b=2或 a = 2 , b = 1 a=2,b=1 a=2,b=1),若能到达这个网格中的任意一节节点,则 p ( a , b ) = = 1 p(a,b)==1 p(a,b)==1
求 ∑ i = 1 n ∑ j = 1 n p ( i , j ) \sum _{i=1}^n\sum _{j=1}^n p(i,j) ∑i=1n∑j=1np(i,j)
数据范围:
20
%
,
n
≤
3000
20\%,n\leq 3000
20%,n≤3000
50
%
,
n
≤
1
0
7
50\%,n\leq 10^7
50%,n≤107
100
%
,
n
≤
1
0
11
100\%,n\leq 10^{11}
100%,n≤1011
S o l u t i o n Solution Solution
20 p t s 20pts 20pts
能从
(
x
,
y
)
(x,y)
(x,y)移动到任意一个格子的充要条件是它能位移到
(
x
+
1
,
y
)
(x+1,y)
(x+1,y)或
(
x
,
y
+
1
)
(x,y+1)
(x,y+1)
也就是要找出一种位移方案,使得这么多步后,位移到一个格子
(
x
+
m
a
+
n
b
,
y
+
e
a
+
f
b
)
(x+ma+nb,y+ea+fb)
(x+ma+nb,y+ea+fb),使得
m
a
+
n
b
=
1
ma+nb=1
ma+nb=1且
e
a
+
f
b
=
0
ea+fb=0
ea+fb=0,对于前者,我们很容易得到
g
c
d
(
a
,
b
)
=
1
gcd(a,b)=1
gcd(a,b)=1
对于后者,略加思索,可以发现: a , b a,b a,b不能同时为奇数
如此这般,我们预处理3000以内的这样的 a , b a,b a,b,为了节省效率,我们只算 a > b a>b a>b的,最后再乘二即可
时间复杂度: O ( n 2 l o g n ) O(n^2logn) O(n2logn) 实际上常数较小,可以拿到 20 p t s 20pts 20pts
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define ULL unsigned long long
using namespace std;int Gcd[3010][3010],k,T;
ULL sum,s[3010],n;
inline int gcd(int x,int y)
{
if(Gcd[x][y]) return Gcd[x][y];//似乎没有多大用处的记忆化
return Gcd[x][y]=y?gcd(y,x%y):x;
}
inline LL read()
{
char c;LL d=1,f=0;
while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
return d*f;
}
signed main()
{
for(register int i=1;i<=3000;i++)
{
for(register int j=1;j<=i;j++)
{
if((i&1)&&(j&1)) continue;
sum+=gcd(i,j)==1;
}
s[i]=sum;
}
T=read();
while(T--)
{
n=read();
printf("%llu\n",s[n]*2);
}
}
50 p t s 50pts 50pts
假设我们已经确定了
a
a
a,考虑怎样的
b
b
b是合法的
刚刚我们已经得出了
a
,
b
a,b
a,b互质,但又有
a
,
b
a,b
a,b不同时为奇数这个限制
那么假设
a
a
a是一个偶数,则它的贡献是不大于它且与它互质的自然数,即
φ
(
a
)
\varphi(a)
φ(a)
假设
a
a
a是一个奇数,则它的贡献是不大于它且与它互质的偶数,这貌似很难办,考场的时候我是推规律推出它为
φ
(
2
a
)
2
\frac {\varphi(2a)}2
2φ(2a)【有能力证明的dalao可以在下方提一下】
如此,我们只需要线性筛出 2 × 1 0 7 2\times 10^7 2×107内的 φ \varphi φ,求个前缀和即可
时间复杂度: O ( n ) O(n) O(n),可以拿到 50 p t s 50pts 50pts
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define ULL unsigned long long
using namespace std;int T,phi[20000010],prime[2000010],vis[20000010],m;
ULL n,s[10000010];
inline LL read()
{
char c;LL d=1,f=0;
while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
return d*f;
}
inline void prework()
{
phi[1]=1;
for(register int i=2;i<=20000000;i++)
{
if(vis[i]==0) {vis[i]=i;prime[++m]=i;phi[i]=i-1;}
for(register int j=1;j<=m&&prime[j]*i<=20000000;j++)
{
if(prime[j]>vis[i]) break;
vis[i*prime[j]]=prime[j];
phi[i*prime[j]]=phi[i]*(i%prime[j]?prime[j]-1:prime[j]);
}
}
return;
}
signed main()
{
prework();
for(register int i=1;i<=1e7;i++)
{
if(i&1) s[i]=phi[i*2]/2;
else s[i]=phi[i];
s[i]+=s[i-1];
}
T=read();
while(T--)
{
n=read();
printf("%llu\n",s[n]*2);
}
}
100 p t s 100pts 100pts
基于 50 p t s 50pts 50pts的思路,多利用些欧拉函数的性质即可
若
i
i
i是奇数,
φ
(
2
i
)
=
φ
(
i
)
\varphi(2i)=\varphi(i)
φ(2i)=φ(i)
若
i
i
i是偶数,
φ
(
2
i
)
=
2
φ
(
i
)
\varphi(2i)=2\varphi(i)
φ(2i)=2φ(i)
当
i
i
i为偶数时,
w
i
=
φ
(
i
)
w_i=\varphi(i)
wi=φ(i)
当
i
i
i为奇数时,
w
i
=
φ
(
2
i
)
2
=
φ
(
i
)
2
w_i=\frac{\varphi(2i)}2=\frac{\varphi(i)}2
wi=2φ(2i)=2φ(i)
设 S ( n ) S(n) S(n)表示答案
则 S ( n ) = 2 ∑ i = 1 n w i S(n)=2\sum_{i=1}^n w_i S(n)=2∑i=1nwi,那么 S ( n ) S(n) S(n)就等于所有偶数的 φ \varphi φ和的两倍+所有奇数的 φ \varphi φ的和
我们可以直接用杜教筛算出奇数+偶数的 φ \varphi φ和(即 φ \varphi φ的前缀和),然后考虑补上没算的偶数的贡献,这一段的贡献即为 S ( ⌊ n 2 ⌋ ) S(\lfloor \frac n2\rfloor) S(⌊2n⌋),递归处理即可,层数显然是 l o g log log层的
时间复杂度: O ( n 2 3 l o g n ) O(n^{\frac 2 3}log n) O(n32logn),可以通过本题
C o d e Code Code
#include<map>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ULL unsigned long long
using namespace std;
ULL T,prime[2000010],vis[20000010],m,n,phi[20000010],s[20000010];
map<ULL,ULL>Sphi;
inline ULL read()
{
char c;ULL d=1,f=0;
while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
return d*f;
}
inline void prework()
{
phi[1]=1;
for(register int i=2;i<=20000000;i++)
{
if(vis[i]==0) {vis[i]=i;prime[++m]=i;phi[i]=i-1;}
for(register int j=1;j<=m&&prime[j]*i<=20000000;j++)
{
if(prime[j]>vis[i]) break;
vis[i*prime[j]]=prime[j];
phi[i*prime[j]]=phi[i]*(i%prime[j]?prime[j]-1:prime[j]);
}
}
for(register int i=1;i<=20000000;i++)
{
if(i&1) s[i]=phi[i*2]/2;
else s[i]=phi[i];
s[i]+=s[i-1];
phi[i]+=phi[i-1];
}
return;
}
inline ULL Getphi(ULL n)
{
if(n<=20000000) return phi[n];
if(Sphi[n]) return Sphi[n];
ULL res=0;
if(n%2==0) res=n/2*(n+1);
else res=(n+1)/2*n;//这一段要这样子弄一弄,不然可能会爆
for(ULL l=2,r;l<=n;l=r+1)
{
r=n/(n/l);
res-=(r-l+1)*Getphi(n/l);
}
return Sphi[n]=res;
}
inline ULL S(ULL n)
{
if(n<=1) return 0;
return Getphi(n)+S(n/2);
}
signed main()
{
prework();
T=read();
while(T--)
{
n=read();
printf("%llu\n",S(n));
}
}