洛谷 P6860 - 象棋与马(找性质+杜教筛)

题面传送门

首先我们来探究一下什么样的 \((a,b)\) 满足 \(p(a,b)=1\)。不难发现只要点 \((1,0)\) 能够到达,那么网格上所有点都能到达,因为由于 \((1,0)\) 能够到达,将坐标轴旋转一下 \((0,1)\) 也能到达,因此对于坐标系中任意一点 \((x,y)\),重复 \(x\)\((0,0)\to(1,0)\) 的过程,再重复 \(y\)\((0,0)\to(0,1)\) 的过程就能够到达 \((x,y)\)

其次,注意到本质不同的移动向量只有四个,即 \((a,b),(b,a),(-a,b),(-b,a)\),我们假设这四个向量分别移动了 \(x,y,z,w\) 次(注意,这边 \(x,y,z,w\) 不一定要 \(\ge 0\),如果 \(x<0\) 则表示沿着 \((-a,-b)\) 向量移动了 \(-x\) 次,很好理解),那么能够到达 \((1,0)\) 的充要条件就是 \(\exists x,y,z,w\in\mathbb{Z},\begin{cases}ax+by-az-bw=1\\bx+ay+bz+aw=0\end{cases}\),上下两式相加可得 \((a+b)(x+y)+(a-b)(w-z)=1\),上下两式相减可得 \((a-b)(x-y)-(a+b)(z+w)=1\),发现都可以转化为 \((a-b)x+(a+b)y=1\) 的方程,根据斐蜀定理知该方程存在整数解的充要条件为 \(\text{gcd}(a-b,a+b)=1\),显然若 \(\text{gcd}(a,b)>1\) 就无解了,并且如果 \(a\equiv b\pmod{2}\) 也无解,因为 \(a-b,a+b\) 都是 \(2\) 的倍数。否则显然有 \(\gcd(a,a+b)=1\),而由 \(2\nmid a+b\) 可知 \(\gcd(2a,a+b)=1\),再辗转相除一下可得 \(\gcd(a-b,a+b)=1\),当然这只是说明了方程 \((a-b)x+(a+b)y=1\) 有解,不能保证一定存在符合要求 \(x,y,z,w\),不过我们仔细想想,我们由于 \(a\not\equiv b\pmod{2}\),必然有 \(a+b\equiv a-b\equiv 1\pmod{2}\),因此我们构造出的解 \(x_0,y_0\) 显然满足 \(x_0\not\equiv y_0\pmod{2}\),而显然同余方程组还存在一组解 \((x_0-(a-b),y_0-(a+b))\),因此我们考虑令 \(\begin{cases}x+y=y_0\\x-y=x_0-(a-b)\\w-z=x_0\\w+z=(a+b)-y_0\end{cases}\),那么显然 \(y_0\)\(x_0-(a-b)\)\(x_0\)\((a+b)-y_0\) 都是同奇偶的,因此就存在符合要求的 \(x,y,z,w\) 了,综上我们得出了结论 \(p(a,b)=[\gcd(a,b)=1\land 2\nmid a+b]\)

接下来考虑计算答案,显然答案等于 \(2\sum\limits_{a\text{为奇数}}\sum\limits_{b<a\text{且}b\text{为偶数}}[\gcd(a,b)=1]+2\sum\limits_{a\text{为偶数}}\sum\limits_{b<a\text{且}b\text{为奇数}}[\gcd(a,b)=1]\),考虑对于一个固定的 \(a\) 的贡献,显然若 \(a\) 为偶数,那么所有 \(<a\) 且与 \(a\) 互质的数都是奇数,贡献为 \(\varphi(a)\),否则贡献就是所有 \(<a\) 且与 \(a\) 互质的偶数,显然对于所有与 \(a\) 互质的偶数 \(x\) 都有 \(\gcd(a,a+x)=1\),而由 \(a+x\) 为奇数可知 \(\gcd(2a,a+x)=1\),并且所有 \(<2a\) 且与 \(2a\) 互质的数 \(v\) 都有 \(v-a\) 为偶数,因此 \(x\) 的个数就是在 \([a,2a)\) 中且与 \(2a\) 互质的数的个数,这个显然等于 \(\dfrac{\varphi(2a)}{2}\),而显然 \(\varphi(2a)=\varphi(a)\),故这个值就等于 \(\dfrac{\varphi(a)}{2}\),因此我们要求的值就是 \([1,n]\) 中所有奇数的 \(\varphi\) 值与所有偶数 \(\varphi\) 值的两倍的和,我们考虑先求 \([1,n]\) 中所有 \(\varphi(i)\) 的和,这样会少算一遍偶数的 \(\varphi(i)\) 的贡献,考虑这个漏算贡献怎么计算,注意到对于 \([1,n]\) 中的某个偶数 \(x\),若 \(\dfrac{x}{2}\) 为奇数则 \(\varphi(x)=\varphi(\dfrac{x}{2})\),否则 \(\varphi(x)=2\varphi(\dfrac{x}{2})\),因此漏算的贡献恰好又是 \([1,\dfrac{n}{2}]\) 中所有奇数的 \(\varphi\) 值与所有偶数 \(\varphi\) 值的两倍的和,递归计算即可。

最后,此题数据范围高达 \(10^{11}\),因此需用杜教筛求出 \(\varphi(x)\) 的前缀和,这个知识点大约是我半年前学的了罢,现在已经几乎忘掉了,所以感谢这道题让我回忆起了遗忘的知识点。根据狄利克雷卷积有 \(\varphi*I=id\),考虑 \(id(x)\) 的前缀和 \(\sum\limits_{i=1}^nid(i)\),我们将每个 \(id(i)\) 都写成关于 \(\varphi(x)\) 的求和式,即 \(\sum\limits_{i=1}^nid(i)=\sum\limits_{i=1}^n\sum\limits_{j|i}\varphi(j)\),交换求和号,先枚举 \(j\) 可得 \(\sum\limits_{i=1}^nid(i)=\sum\limits_{j=1}^n\sum\limits_{i=1}^{\lfloor\frac{n}{j}\rfloor}\varphi(i)\),将 \(j=1\) 单独提出来可得 \(\sum\limits_{i=1}^nid(i)=\sum\limits_{i=1}^n\varphi(i)+\sum\limits_{j=2}^n\sum\limits_{i=1}^{\lfloor\frac{n}{j}\rfloor}\varphi(i)\),记 \(\varphi_s(n)=\sum\limits_{i=1}^n\varphi(i)\),那么 \(\sum\limits_{i=1}^nid(i)=\varphi_s(n)+\sum\limits_{j=2}^n\varphi_s(\lfloor\dfrac{n}{j}\rfloor)\),前面 \(\sum\limits_{i=1}^nid(i)\) 显然等于 \(\dfrac{n(n+1)}{2}\),后面 \(\sum\limits_{j=2}^n\varphi_s(\lfloor\dfrac{n}{j}\rfloor)\) 整除分块递归处理即可,根据 P6788 这样整除分块套整除分块复杂度是 \(n^{3/4}\) 的,不过预处理 \([1,n^{2/3}]\)\(\varphi_s(n)\) 可将复杂度降到 \(n^{2/3}\)

最后此题有一个坑点,就是在求 \(id(n)\) 的前缀和 \(\dfrac{n(n+1)}{2}\) 不能直接写 1ull*n*(n+1)/2,因为对于 \([0,2^{64})\) 的整数 \(v\),在 \([0,2^{64})\) 中存在两个整数 \(x\) 满足 \(2x\equiv v\pmod{2^{64}}\),而你这样直接除就默认是 \(<2^{63}\) 的那个 \(x\) 了,会出问题,正确写法是分 \(n\) 为奇数和偶数分别处理,先除再乘(NOIP2020 悲惨回忆),这样就不会出问题了。

const int MAXV=2e7;
int phi[MAXV+5],pr[MAXV/10+5],prcnt=0;
u64 sum[MAXV+5];bitset<MAXV+5> vis;
void sieve(int n){
	phi[1]=1;
	for(int i=2;i<=n;i++){
		if(!vis[i]){pr[++prcnt]=i;phi[i]=i-1;}
		for(int j=1;j<=prcnt&&pr[j]*i<=n;j++){
			vis[pr[j]*i]=1;
			if(i%pr[j]==0){phi[i*pr[j]]=phi[i]*pr[j];break;}
			else phi[i*pr[j]]=phi[i]*phi[pr[j]];
		}
	}
	for(int i=1;i<=n;i++) sum[i]=sum[i-1]+phi[i];
}
unordered_map<ll,u64> mem;
u64 getsum(ll n){
	if(n<=MAXV) return sum[n];
	if(mem[n]) return mem[n];
	u64 ret;
	if(n%2) ret=1ull*(n+1)/2*n;
	else ret=1ull*n/2*(n+1);
	for(ll l=2,r;l<=n;l=r+1){
		r=(n/(n/l));ret-=1ull*getsum(n/l)*(r-l+1);
	} return mem[n]=ret;
}
u64 solve(ll n){
	if(n==1) return 0;
	return solve(n/2)+getsum(n);
}
int main(){
	sieve(MAXV);int qu;scanf("%d",&qu);
	while(qu--){ll n;scanf("%lld",&n);printf("%llu\n",solve(n));}
	return 0;
}
posted @ 2021-04-08 19:33  tzc_wk  阅读(68)  评论(0)    收藏  举报