数论分块专题复习(余数求和+模积和+Ice Rain+The Fool)

15 篇文章 1 订阅
9 篇文章 1 订阅

前提知识复习

整除分块是用于快速处理形似下列式子的方法,是解决莫比乌斯反演类题目需要掌握的前提知识
∑ i = 1 n ⌊ n i ⌋ \sum_{i=1}^n\lfloor\frac{n}{i}\rfloor i=1nin
但是本篇博客的例题是特别特别板的,不会涉及莫比乌斯反演,请dalao们出门左转别浪费时间,蟹蟹
回归正题,很显然上面的式子可以 O ( n ) O(n) O(n)得到答案
但是,在某些题目中,毒瘤出题人将数据加强到了 1 0 10 10^{10} 1010以上
这个时候我们就无法通过 O ( n ) O(n) O(n) 的解法来得到答案
我们需要一个 O ( n ) O(\sqrt{n}) O(n )的更为优秀的解法

对于单一的 ⌊ n i ⌋ \lfloor\frac{n}{i}\rfloor in,某些地方的值是相同的,并且呈块状分布
通过深入的探求规律与严密推理以及暴力打表与幸运瞎猜 ,最后惊奇的发现这些块状分布的值是有规律的
对于一个块,假设它的起始位置的下标为 l l l,那么可以得到的是,它的结束位置的下标为在这里插入图片描述
代码表示即为

r = n / ( n / l );

分块如果非要安排一个模板的话,那就是一个 f o r for for循环了
在这里插入图片描述

int calc( int n ) {
	int ans = 0;
	for( int l = 1, r;l <= n;l = r + 1 ) {
		r = n / ( n / l );
		//根据题目要求进行计算 
	}
	return ans;
}

有的时候不同的题目可能出现 n / l = = 0 n/l==0 n/l==0的情况,为了防止程序挂掉,我们也可以这么写

int calc( int n ) {
	int ans = 0;
	for( int l = 1, r;l <= n;l = r + 1 ) {
		if( k / l ) r = min( k / ( k / l ), n );
		else r = n;
		//根据题目要求进行计算
	}
	return ans;
}

具体的就用例题来体会吧

T1:余数求和

title

添加链接描述

solution

G ( n , k ) = ∑ i = 1 n k   m o d   i = ∑ i = 1 n ( k − ⌊ k i ⌋ ∗ i ) = ∑ i = 1 n k − ∑ i = 1 n ⌊ k i ⌋ ∗ i G(n,k)= ∑_{i=1}^n k\ mod\ i=\sum_{i=1}^n(k-\lfloor\frac{k}{i}\rfloor*i)=\sum_{i=1}^nk-\sum_{i=1}^n\lfloor\frac{k}{i}\rfloor*i G(n,k)=i=1nk mod i=i=1n(kiki)=i=1nki=1niki
前面的求和式子可以很直观地得到 ∑ i = 1 n k = n ∗ k \sum_{i=1}^nk=n*k i=1nk=nk
后面的求和式子我们令 l l l表示这个块的开始下标, r r r为这个块的结束下标, T = ⌊ n i ⌋ T=\lfloor\frac{n}{i}\rfloor T=in,则该块里面的值为:
∑ i = l r T ∗ i = ∑ i = l r T − ∑ i = l r i \sum_{i=l}^rT*i=\sum_{i=l}^rT-\sum_{i=l}^ri i=lrTi=i=lrTi=lri
答案显而易见了吧,第一个求和就是个定值 ( r − l + 1 ) ∗ T (r-l+1)*T (rl+1)T,后面的求和就是等差数列

计算方法:首项加末项乘以项数除以二=> ( l + r ) ∗ ( r − l + 1 ) / 2 (l+r)*(r-l+1)/2 (l+r)(rl+1)/2

code

#include <cstdio>
#include <iostream>
using namespace std;
#define int long long
signed main() {
	int n, k;
	scanf( "%lld %lld", &n, &k );
	int ans = n * k;
	for( int l = 1, r;l <= n;l = r + 1 ) {
		if( k / l ) r = min ( k / ( k / l ), n );
		else r = n;
		ans -= ( r - l + 1 ) * ( k / l ) * ( l + r ) / 2;
	}
	printf( "%lld", ans );
	return 0;
}

T2:Ice Rain

title

添加链接描述

solution

读完题后是不是
在这里插入图片描述
一模一样的吧,这种sb题 你加一个无线输入操作就 A C AC AC p a s s pass pass,下一个!!

code

#include <cstdio>
#include <iostream>
using namespace std;

int main() {
	long long n, k;
	while( ~ scanf( "%lld %lld", &n, &k ) ) {
		long long ans = n * k;
		for( long long l = 1, r;l <= n;l = r + 1 ) {
			if( k / l ) r = min( k / ( k / l ), n );
			else r = n;
			ans -= ( k / l ) * ( l + r ) * ( r - l + 1 ) / 2;
		}
		printf( "%lld\n", ans );
	}
	return 0;
}

T3:The Fool

title

添加链接描述

solution

一样的吧?差不多的吧?如果你没有一点思路的话,证明我写的太差 没有学懂哦~
∑ i = 1 n ⌊ n i ⌋ ∑_{i=1}^n \lfloor\frac{n}{i}\rfloor i=1nin,先分出每个块,然后再等差数列求和,加在一起最后判断, v a n van van
在这里插入图片描述

code

#include <cstdio>
int main() {
	int T;
	scanf( "%d", &T );
	for( int Case = 1;Case <= T;Case ++ ) {
		long long n;
		scanf( "%lld", &n );
		long long ans = 0;
		for( int l = 1, r;l <= n;l = r + 1 ) {
			r = n / ( n / l );
			ans += ( r - l + 1 ) * ( n / l );
		} 
		if( ans & 1 ) printf( "Case %d: odd\n", Case );
		else printf( "Case %d: even\n", Case );
	}
	return 0;
}

T4:模积和

title

添加链接描述

solution

这道题就是个重头戏了,其实也很简单的
仔细看推导过程!!
a n s = ∑ i = 1 n ∑ j = 1 m ( n   m o d   i ) × ( m   m o d   j ) , i ≠ j ans=∑_{i=1}^n∑_{j=1}^m(n\ mod\ i)×(m\ mod\ j),i≠j ans=i=1nj=1m(n mod i)×(m mod j),i=j
用容斥拆开把 i = j i=j i=j的情况减掉即可
a n s = ∑ i = 1 n ∑ j = 1 m ( n   m o d   i ) × ( m   m o d   j ) − ∑ i = 1 m i n ( n , m ) ( n   m o d   i ) × ( m   m o d   i ) ans=∑_{i=1}^n∑_{j=1}^m(n\ mod\ i)×(m\ mod\ j)-∑_{i=1}^{min(n,m)}(n\ mod\ i)×(m\ mod\ i) ans=i=1nj=1m(n mod i)×(m mod j)i=1min(n,m)(n mod i)×(m mod i)
直接把显而易见能分块的先分了来,再暴力展开
∑ i = 1 n ( n   m o d   i ) = ∑ i = 1 n n − ∑ i = 1 n ⌊ n i ⌋ ∗ i ∑_{i=1}^n(n\ mod\ i)=\sum_{i=1}^nn-\sum_{i=1}^n\lfloor\frac{n}{i}\rfloor*i i=1n(n mod i)=i=1nni=1nini
∑ j = 1 m ( m   m o d   j ) = ∑ j = 1 m m − ∑ j = 1 m ⌊ m j ⌋ ∗ j ∑_{j=1}^m(m\ mod\ j)=\sum_{j=1}^mm-\sum_{j=1}^m\lfloor\frac{m}{j}\rfloor*j j=1m(m mod j)=j=1mmj=1mjmj
∑ i = 1 m i n ( n , m ) ( n   m o d   i ) × ( m   m o d   i ) = ∑ i = 1 m i n ( n , m ) ( n − ⌊ n i ⌋ ∗ i ) ∗ ( m − ⌊ m i ⌋ ∗ i ) ∑_{i=1}^{min(n,m)}(n\ mod\ i)×(m\ mod\ i)=\sum_{i=1}^{min(n,m)}(n-\lfloor\frac{n}{i}\rfloor*i)*(m-\lfloor\frac{m}{i}\rfloor*i) i=1min(n,m)(n mod i)×(m mod i)=i=1min(n,m)(nini)(mimi)
= ∑ i = 1 m i n ( n , m ) ( n m + ⌊ m i ⌋ ⌊ n i ⌋ i 2 − ⌊ m i ⌋ i − ⌊ n i ⌋ i ) =\sum_{i=1}^{min(n,m)}(nm+\lfloor\frac{m}{i}\rfloor\lfloor\frac{n}{i}\rfloor i^2-\lfloor\frac{m}{i}\rfloor i-\lfloor\frac{n}{i}\rfloor i) =i=1min(n,m)(nm+imini2imiini)
整理综上
在这里插入图片描述
a n s = ( ∑ i = 1 n n − ∑ i = 1 n ⌊ n i ⌋ ∗ i ) ( ∑ i = 1 m m − ∑ i = 1 m ⌊ m i ⌋ ∗ i ) − ∑ i = 1 m i n ( n , m ) ( n m + ⌊ m i ⌋ ⌊ n i ⌋ i 2 − ⌊ m i ⌋ i − ⌊ n i ⌋ i ) ans=(\sum_{i=1}^nn-\sum_{i=1}^n\lfloor\frac{n}{i}\rfloor*i)(\sum_{i=1}^mm-\sum_{i=1}^m\lfloor\frac{m}{i}\rfloor*i)-\sum_{i=1}^{min(n,m)}(nm+\lfloor\frac{m}{i}\rfloor\lfloor\frac{n}{i}\rfloor i^2-\lfloor\frac{m}{i}\rfloor i-\lfloor\frac{n}{i}\rfloor i) ans=(i=1nni=1nini)(i=1mmi=1mimi)i=1min(n,m)(nm+imini2imiini)
最后我们发现 i 2 i^2 i2在分块的时候挂掉了,OMG,怎么办!!,告诉你一个结论

∑ i n i 2 = n ∗ ( n + 1 ) ∗ ( 2 n + 1 ) / 2 ∑_i^ni^2=n∗(n+1)∗(2n+1)/2 ini2=n(n+1)(2n+1)/2

在这里插入图片描述
最后注意避雷!!! p p p不是个质数,稍微用随便找个互质的数搞个逆元就好了

code

#include <cstdio>
#include <iostream>
using namespace std;
#define int long long
#define mod 19940417
int n, m, inv;

int qkpow( int x, int y ) {
	int ans = 1;
	while( y ) {
		if( y & 1 ) ans = ans * x % mod;
		x = x * x % mod;
		y >>= 1;
	}
	return ans;
}

int sqr( int x ) {
	return x * ( x + 1 ) % mod * ( x << 1 | 1 ) % mod * inv % mod;
}

int sum( int l, int r ) {
	return ( l + r ) * ( r - l + 1 ) / 2 % mod;
}

int calc( int n ) {
	int ans = 0;
	for( int l = 1, r;l <= n;l = r + 1 ) {
		r = n / ( n / l );
		ans = ( ans + n * ( r - l + 1 ) % mod - ( n / l ) * sum( l, r ) % mod + mod ) % mod;
	}
	return ans;
}

signed main() {
	inv = qkpow( 6, 17091779 );
	scanf( "%lld %lld", &n, &m );
	int ans = calc( n ) * calc( m ) % mod;
	if( n > m ) swap( n, m );
	for( int l = 1, r;l <= n;l = r + 1 ) {
		r = min( n / ( n / l ), m / ( m / l ) );
		int sum1 = n * m % mod * ( r - l + 1 ) % mod;
		int sum2 = ( n / l ) * ( m / l ) % mod * ( sqr( r ) - sqr( l - 1 ) + mod ) % mod;
		int sum3 = ( n / l * m % mod + m / l * n % mod ) * sum( l, r ) % mod;
		ans = ( ans - ( sum1 + sum2 - sum3 + mod ) % mod + mod ) % mod;
	}
	printf( "%lld", ans );
	return 0;
}

走了,题还没做完…
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值