数论学习总结(包会,含例题,未完成)

数论学习总结(未完成)

模版总结

快速幂

快速幂算法(Exponentiation by Squaring)是一种用于计算幂运算的高效算法。它通过将指数进行二进制拆分,并利用指数的二进制表示形式来减少乘法和幂运算的次数,从而提高计算速度。那么具体怎么做呢?

  1. 将指数表示成二进制
  2. 从右向左遍历二进制数,如果当前位为 1 1 1​,则将底数乘以自身的平方,否则只将底数平方
  3. 每次计算完毕后,将指数右移一位(相当于除以 2 2 2
  4. 重复步骤 2 2 2 和步骤 3 3 3,直到指数变为 0 0 0​。

代码实现:

long long qp(long long a, long long b,long long c)
{
	if(b==0) 
		return 1;
	if(b==1) 
		return a%c;
	long long t=qp(a,b/2,c)%c;
	return (((t*t)%c)*(b%2==0?1:a%c))%c;
}
质数

质数是什么呢?简而言之,就是只能被 1 1 1 和他自己整除的正整数,其他正整数则为合数,但是呢? 1 1 1 既不是质数,也不是合数,妥妥反骨。

试除法

那么这个呢就是判断一个数是否是质数的一个工具,时间复杂度是 O ( log ⁡ n ) O( \log n) O(logn)。其实就是开根号遍历,如果除得尽就不是,如果都除不尽就是质数。

#include<bits/stdc++.h>
using namespace std;
bool is_prime(int x)  // 判定质数
{
    if (x < 2) return false;
    for (int i = 2; i <= x / i; i ++ )
        if (x % i == 0)
            return false;
    return true;
}
int main()
{
    int x;
    cin>>x;
    if(is_prime(x))
    {
        cout<<"YES"<<endl;
    }
    else
    {
        cout<<"NO"<<endl;
    }
    return 0;
}

那我们针对判断质数的方法发出了质问,有没有更优的算法呢?你别说,有其实还是有的。我们根据一个定理:质数密度估计(质数定理): n n n 较大时, [ 1 , n ] \left[1,n\right] [1,n] 中质数的数量接近为 O ( n ÷ ln ⁡ n ) O(n \div \ln n) O(n÷lnn)。因此如果我们预处理 [ 2 , ⌊ n ⌋ ] \left[2,\left \lfloor \sqrt{n} \right \rfloor \right ] [2,n ]的质数表,每次用他们来试除,就可以做到 O ( n ÷ ln ⁡ n ) O(\sqrt{n} \div \ln n) O(n ÷lnn)。但是还有别的更快的方法,不过我太弱了,先不说了。

唯一分解定理

唯一分解定理其实就是 任何正整数 n n n 都可以被分解成若干个质数的幂次相乘,简单表示成这样: ∏ i p i c i \prod_{i}p_i^{c_i} ipici

那么又有一个简单的性质,分解至多到 O ( log ⁡ n ) O(\log n) O(logn),因为至少至少全是 2 2 2

gcd和lcm
int gcd(int a, int b)
{
	return b?gcd(b,a%b):a;
}

gcd表示两个数的最大公共因数,然后怎么求呢?可以使用stl库里面的__gcd或者是使用辗转相除法解决,下面会在exgcd之前提到。时间复杂度 O ( log ⁡ max ⁡ ( n , m ) ) O(\log \max(n,m)) O(logmax(n,m)) 解决。然后 lcm 就是最小的公共倍数。然后我们不妨换一个角度去看 lcm 和 gcd。设 n = ∏ p i c i , m = ∏ p i d i n= \prod p_i^{c_i},m=\prod p_i^{d_i} n=picim=pidi。那么 gcd ⁡ = ∏ p i min ⁡ ( c i , d i ) ,  l c m = ∏ p i max ⁡ ( c i , d i ) \gcd = \prod p_i^{\min(c_i,d_i)},\ lcm = \prod p_i^{\max(c_i,d_i)} gcd=pimin(ci,di) lcm=pimax(ci,di)

所以我们得到一个非常简单的式子:   g c d ×   l c m = n × m \ gcd \times \ lcm =n \times m  gcd× lcm=n×m

埃氏筛

相信有很多同学看到埃氏筛这个名字的时候都会以为是埃及人发明的,事实上这只是一个人的名字而已。那么他的思想是什么呢?也就是说先假定 ≥ 2 \ge 2 2 的数都是质数,从小到大考虑每一个 i i i ,若是质数,则 i i i 的倍数都不是质数,全部筛掉。时间复杂度 O ( n ln ⁡ ln ⁡ n ) O(n \ln \ln n) O(nlnlnn)​,代码如下:

const int maxn=2e6+6;
bool isprime[maxn];
void seive(){
    memset(isprime,true,sizeof(isprime));
    isprime[0]=isprime[1]=false;
    for(int i=2;i<=maxn-6;i++){
        if (isprime[i]) {
            for (int j = i * i; j <= maxn-6; j += i) {
                isprime[j] = false;
            }
        }
    }
}
欧拉筛/线性筛

欧拉筛是一种优秀的算法,他的复杂度是线形的,你可以这么认为就是在埃氏筛的基础上做了一个优化,也就是说 6 6 6 这个合数, 2 2 2 会被筛一次, 3 3 3 也会被筛一次,那么在欧拉筛里面,记录一个数组,如果最小的质因子被筛掉了,那么剩下的不用筛了,肯定不是质数。

const int N = 1e8 + 3;
bool isprime[N];
int prime[N],cnt;
void ola(int n) {
	memset(isprime, true, sizeof(isprime));
	isprime[1] = 0;
 
	for (int i = 2; i <= n; i++) {
		if (isprime[i]) prime[++cnt] = i;//如果i没有被前面的数筛掉,则i是素数
 
		for (int j = 1; j<=cnt&&prime[j] <= n/i; j++) {//j枚举已经筛出的素数,该循环起到筛掉枚举素数的倍数的作用
			isprime[i * prime[j]] = 0;//把i*prime[j]筛掉
			if (i % prime[j] == 0) break;//保证不会重复筛
		}
	}
}
神秘的筛法

先问一个问题给大家:求出 [ l , r ] \left[l,r\right] [l,r] 之间的所有素数,但是 ( l ≤ r ≤ 1 0 12 , r − l ≤ 1 0 6 ) (l \leq r \leq 10^{12},r-l \leq 10^6) (lr1012rl106)

很容易的就想到了欧拉筛或者埃氏筛,但是 l l l 实在是太大了,根本无法使用他们,但是我们又注意到了 r − l ≤ 1 0 6 r-l \leq 10^6 rl106。我们可以从这里作为突破口,再考虑到 ≤ r \leq r r 的合数必然有 ≤ r \leq \sqrt{r} r 的质因子,那我们先筛出来 [ 1 , ⌊ r ⌋ ] \left[1,\left \lfloor \sqrt{r} \right \rfloor\right] [1r ] 的所有质数,再用这些质数去筛就可以了。只是复杂度比较恶习 O ( r + ( r − l ) log ⁡ r ) O(\sqrt{r}+(r-l) \log r) O(r +(rl)logr) O ( r ln ⁡ ln ⁡ r + ( r − l ) log ⁡ r ) O(\sqrt{r} \ln \ln r+(r-l) \log r) O(r lnlnr+(rl)logr)

数论函数和积性函数

什么是数论函数的?也就是定义域为正整数的函数叫做数论函数。我们举几个接下来会很常用的函数,如欧拉函数 φ ( n ) \varphi(n) φ(n),莫比乌斯函数 μ ( n ) \mu(n) μ(n),单位函数 ϵ ( n ) \epsilon(n) ϵ(n)。那么这些都是什么意思呢?

  • φ ( n ) \varphi(n) φ(n) 表示 [ 1 , n ] [1,n] [1,n] 中与 n n n 互质数的数的数量。读作
  • μ ( n ) = { ( − 1 ) k , n = p 1 p 2 … p k , ( p i 是质数 ) 0 , n 的某个质因子的指数大于 2 \mu(n)=\begin{cases}(-1)^k,n=p_1p_2 \dots p_k,(p_i是质数)\\0,n的某个质因子的指数大于2\\\end{cases} μ(n)={(1)kn=p1p2pk(pi是质数)0n的某个质因子的指数大于2,读作miu​
  • ϵ ( n ) \epsilon(n) ϵ(n) 仅当 n = 1 n=1 n=1 时为 1 1 1,其他情况都是 0 0 0。读作epsilon。

那什么又是积性函数呢?若一个积性函数 f f f 满足,对所有互质的 n , m n,m nm,都满足 f ( n m ) = f ( n ) f ( m ) f(nm)=f(n)f(m) f(nm)=f(n)f(m),则称 f f f 是一个积性函数,上面的三个函数均是非常普通的积性函数。另外如果对所有的 n , m n,m nm,都满足 f ( n m ) = f ( n ) f ( m ) f(nm)=f(n)f(m) f(nm)=f(n)f(m),则称 f f f 是一个完全积性函数,所有积性函数都可以用筛来简单地求解。

求欧拉函数

我们不妨用欧拉筛来求解,我们可以分类讨论。

  • i i i 是质数时,显然 φ ( i ) = n − 1 \varphi(i)=n-1 φ(i)=n1
  • i ≢ 0 ( m o d p ) i \not \equiv0\pmod{p} i0(modp) 时, i i i p p p 是互质的,故 φ ( i p ) = φ ( i ) φ ( p ) = ( p − 1 ) φ ( i ) \varphi(ip)=\varphi(i)\varphi(p)=(p-1)\varphi(i) φ(ip)=φ(i)φ(p)=(p1)φ(i)
  • i ≡ 0 ( m o d p ) i \equiv0\pmod{p} i0(modp) 时, p p p i i i 的一个因子。

i i i 的唯一分解中 p p p 的指数是 c c c ,因为 φ ( p c ) = ( p − 1 ) p c − 1 \varphi(p^c) =(p-1)p^{c-1} φ(pc)=(p1)pc1,因此 φ ( i p ) = p φ ( i ) \varphi(ip)=p\varphi(i) φ(ip)=(i)​。

代码如下:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int a; 
	cin>>a;
	int res=a;
	for(int i=2;i<=a/i;i++)
	{
		if(a%i==0)
		{
			while(a%i==0)
				a/=i;
			res=res/i*(i-1);
		}
	}
	if(a>1)
		res=res/a*(a-1);
	cout<<res<<endl;
	return0;
}
求莫比乌斯函数

看起来这个名字非常的高大尚,事实上他和欧拉函数非常相似,甚至还要简单一些,我们依旧可以分类讨论。

  • i i i 是质数时,显然 μ ( i ) = − 1 \mu(i)=-1 μ(i)=1
  • i ≢ 0 ( m o d p ) i \not \equiv0\pmod{p} i0(modp) 时, i i i p p p 是互质的,故 μ ( i p ) = μ ( i ) μ ( p ) = − μ ( i ) \mu(ip)=\mu(i)\mu(p)=-\mu(i) μ(ip)=μ(i)μ(p)=μ(i)
  • i ≡ 0 ( m o d p ) i \equiv0\pmod{p} i0(modp) 时, p p p i i i 的一个因子,故 μ ( i p ) = 0 \mu(ip)=0 μ(ip)=0

代码如下:

int miu(long long x) 
{
	int sum=0;
	for(long long i=2;i<=x/i;i++)
	{
		int cnt=0;
		if(x%i==0)
		{		
			while(x%i==0)
			{
				cnt++;
				x/=i;
			}
			if(cnt>1)
				return 0;
			sum++;
		}
	}
	if(x!=1)
		sum++;
	return(sum&1)?-1:1;
}
函数的基本公式

欧拉函数和莫比乌斯函数与这么几个常见的公式,后面的例题可能会用到,大家一定要记住。

欧拉公式:记 $ n=\prod p_i^{c_i}$,则 φ ( n ) = ∏ ( p i − 1 ) p i c i − 1 \varphi(n)=\prod(p_i-1)p_i^{c_i-1} φ(n)=(pi1)pici1

那么他的用处是什么呢,就是快速计算一个 φ ( n ) \varphi(n) φ(n)

μ \mu μ 的性质: ∑ d ∣ n μ ( d ) = [ n = 1 ] \sum_{d\mathrel{|}n}\mu(d)=\left[n=1\right] dnμ(d)=[n=1]。其中 [ n = 1 ] [n=1] [n=1] 表示当 n = 1 n=1 n=1 时为1,否则为0。

数论分块

数论分块在后面的很多题目里都会用到,我们先来讲一下。数论分块可以快速计算一些含有除法向下取整的和式(即形如 ∑ i = 1 n f ( i ) g ( ⌊ n i ⌋ ) \sum_{i=1}^{n}f(i)g(\left \lfloor \frac{n}{i}\right \rfloor) i=1nf(i)g(in) 这样的和)。同时也可以在 O ( 1 ) O(1) O(1) 的时间复杂度下计算 f ( r ) − f ( l ) f(r)-f(l) f(r)f(l) 或预处理出 f f f 的前缀和,数论分块就只需要话 O ( n ) O(\sqrt{n}) O(n ) 的时间完成。其余的引理我们这里就不作介绍,有兴趣的同学自己去看就好了,其实是我太菜了,嘻嘻(#.#)。最终的结论就是:

⌊ n i ⌋ = ⌊ n j ⌋ \left \lfloor \frac{n}{i} \right \rfloor=\left \lfloor \frac{n}{j} \right \rfloor in=jn

证明我们略微带一点:

首先令 k = ⌊ n i ⌋ k=\left \lfloor \frac{n}{i} \right \rfloor k=in,我们就可以知道 k ≤ n i k \leq \frac{n}{i} kin

∴ ⌊ n k ⌋ ≥ ⌊ n n i ⌋ = ⌊ i ⌋ = i \therefore \left \lfloor \frac{n}{k} \right \rfloor \ge \left \lfloor \frac{n}{\frac{n}{i}} \right \rfloor=\left \lfloor i \right \rfloor=i kninn=i=i

∴ j = max ⁡ 满足条件的所有 i = i max ⁡ = ⌊ n k ⌋ = ⌊ n ⌊ n i ⌋ ⌋ \therefore j=\max满足条件的所有i=i_{\max}=\left \lfloor \frac{n}{k} \right \rfloor=\left \lfloor \frac{n}{\left \lfloor \frac{n}{i} \right \rfloor} \right \rfloor j=max满足条件的所有i=imax=kn=inn

现在知道了他是什么东西,那到底应该怎么去使用他呢?

首先我们先考虑和式,然后在使用数论分块加速,总之就是先求出 f ( i ) f(i) f(i) 的前缀和,然后每次以 [ l , r ] = [ l , ⌊ n ⌊ n i ⌋ ⌋ ] \left[l,r\right]=\left[l,\left \lfloor \frac{n}{\left \lfloor \frac{n}{i} \right \rfloor} \right \rfloor\right] [l,r]=[l,inn] 为一块,分块求贡献相加即可。

下面这一块csdnlatex炸了,我把我写的截图给大家
同余

如果 a   m o d   m = b   m o d   m a\bmod m=b \bmod m amodm=bmodm,那么称 a , b a,b a,b 对模 m m m 同余,写作: a ≡ b ( m o d m ) a\equiv b\pmod m ab(modm)
余数域 0 , 1 … p − 1 0,1 \dots p-1 0,1p1 被称作 p p p 的剩余系。
接下来我们来讲一讲同余的一些结论。

  1. a ≡ b ( m o d m ) ⇔ m ∣ ( a − b ) a \equiv b \pmod m \Leftrightarrow m\mid(a-b) ab(modm)m(ab)
  2. a ≡ b ( m o d m ) a \equiv b \pmod m ab(modm) c ] e q u i v d ( m o d m ) c ]equiv d \pmod m c]equivd(modm),那么 a + c ≡ b + d ( m o d m ) a+c\equiv b+d \pmod m a+cb+d(modm) a × c ≡ b × d ( m o d m ) a \times c \equiv b \times d\pmod m a×cb×d(modm)(可以加,减,乘,但是绝对不能除)
  3. a × c ≡ b × c ( m o d m ) a \times c \equiv b \times c \pmod m a×cb×c(modm),那么 a ≡ b ( m o d m ( m , c ) ) a \equiv b \pmod{m(m,c)} ab(modm(m,c)),特别的,当 gcd ⁡ ( m , c ) = 1 \gcd(m,c)=1 gcd(m,c)=1 时, a ≡ b ( m o d m ) a \equiv b \pmod m ab(modm)
  4. a ≡ b ( m o d m ) a \equiv b \pmod m ab(modm),那么对于 m m m 的任意一个正因数 n n n,都有 a ≡ b ( m o d n ) a \equiv b \pmod n ab(modn)
  5. a ≡ b ( m o d m ) a \equiv b \pmod m ab(modm) 并且 a ≡ b ( m o d n ) a \equiv b \pmod n ab(modn),那么 a ≡ b ( m o d [ m , n ] ) a \equiv b \pmod{\left[m,n\right]} ab(mod[m,n])
费马小定理

这里我是真的不想打了,自己看看吧。
请添加图片描述

看过初等数论的同学就可以知道这里用的是二项式定理,当然归纳法也可以。

裴蜀定理

a , b a,b ab 是整数,且 gcd ⁡ ( a , b ) \gcd(a,b) gcd(a,b),那么对于任意整数 x , y x,y x,y 一定是 d d d 的倍数。特别地,一定存在 a x + b y = d ax+by=d ax+by=d 成立。

证明:

a x + b y ax+by ax+by 的最小正整数的值为 r r r,考虑 a   m o d   r a \bmod r amodr

p = ⌊ a r ⌋ p=\left \lfloor \frac{a}{r} \right \rfloor p=ra,则 a   m o d   r = a − p r a \bmod r=a-pr amodr=apr

带入 r r r,得 a   m o d   r = a − p ( a x + b y ) a \bmod r=a-p(ax+by) amodr=ap(ax+by)

进一步得 a ( 1 − p x ) + b ( − y ) a(1-px)+b(-y) a(1px)+b(y),也是 a x ’ + b y ’ ax^{’} + by^{’} ax+by 的形式。

而考虑到 0 ≤ a   m o d   r < r 0 \leq a \bmod r <r 0amodr<r,以及 r r r a x + b y ax+by ax+by 的最小整数值,

所以 a   m o d   r = 0 a \bmod r=0 amodr=0。即 r r r a a a 的因数。

那么怎么求呢,那就不得不提到我们的 exgcd 了,它相比于费马小定理,不用考虑 p p p 为质数的情况。代码如下:

int exgcd(int a,int b,int &x,int &y)
{
	if(b==0)
	{
		x=1;
		y=0;
		return a;
	}
	intg=exgcd(b,a%b,y,x);
	y-=a/b*x;
	return g;
}

然后这里的话只要求出一种解,求通解就非常简单了,所以我不多说了。然后这还可以求逆元(不会的往下滑),由裴蜀定理可知,若 a a a p p p 互质,那么存在 a x + p y = 1 ax+py=1 ax+py=1,即 a x ≡ 1 ( m o d m ) ax \equiv 1\pmod m ax1(modm),这样子就可以求出 [ 0 , p ) \left[0,p\right) [0,p) 之间 a a a 在模 p p p 意义下的逆元(即这个式子求出来的 p p p)。

逆元

如果关于 x x x 的方程 a x ≡ 1 ( m o d p ) ax\equiv1\pmod p ax1(modp)的一个解为 x 0 x_0 x0,那么就称 x 0 x_0 x0 a a a 在模 p p p 意义下的逆元,记作 x 0 = a − 1 x_0=a^{-1} x0=a1,也记作 x = i n v ( a ) x=inv(a) x=inv(a)

但是逆元不一定存在 a a a 再模 p p p 意义下的逆元在的充要条件为 gcd ⁡ ( a , b ) = 1 \gcd(a,b)=1 gcd(a,b)=1​。

有了逆元,我们做除法的取模只要乘那个数的逆元就可以了,多么伟大的发明啊,QWQ。
求逆元的方法:

单个逆元:

  • 快速幂(FM小定理)
  • exgcd

预处理多个

  • 线性筛
  • 线性递推(公式,如下)

i n v i = ( p − p ÷ i ) × i n v p % i % p inv_i=(p-p \div i) \times inv_{p \% i} \% p invi=(pp÷i)×invp%i%p

例题详解

毕业季

这是一道非常智慧的题目。

简化题意可得:从1~n中取k个数,使这k个数的最大公约数最大

因为两个数成倍数关系时,它们的最大公因数是两数中的较小数,也就是相对来说最大公因数较大。那么也就是说 n ÷ k n \div k n÷k

#include<iostream>
#include<cstdio>
using namespace std;
long long n,k;
int main()
{
    cin>>n>>k;
    cout<<n/k;
    return 0;
}
因子个数和

令f(i)为i的因子个数,求f(1)+f(2)+…+f(n)的值


首先,我们肯定会想到 O ( n ) O(n) O(n) 的方法,但是这一题的数据证明了这一题应该需要 O ( n ) O(\sqrt{n}) O(n )。我知道我的同桌非常聪明,想到了一个神奇的方法,也就是一起算。所以代码长这样。

#include<bits/stdc++.h>
using namespaced std;
int main()
{
	long long n;
	cin>>n;
	long long ans=0;
	for(int i=1;i<=sqrt(n);i++)
	{
		ans+=((long long)i*i)/i*2+1;
	}
	cout<<ans<<endl;
	return 0;
}

但是我作为蒟蒻,想到了数论分块的模版题,然后写了出来,长这样:

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k;
int solve2(int l,int r) 
{ 
	return r-l+1; 
}
int solve()
{
    int l=1,r=0,res=0;
	while(l<=n)
	{
		r=k/(k/l);
		res+=solve2(l,r)*(k/l);
		l=r+1;
	}
	return res;
}
signed main()
{
	scanf("%lld",&n);
	k=n;
	printf("%lld",solve());
}

我认为都还可以,所以都放出来了,大家自己比对。

完全平方数

一个数如果是另一个整数的完全平方,那么我们就称这个数为完全平方数,也称平方数。小A认为所有的平方数都是很perfect的~于是他给了小B一个任务:用任意个不大于n的不同的正整数相乘得到完全平方数,并且小A希望这个平方数越大越好。请你帮助小B告诉小A满足题意的最大的完全平方数。


首先我们要知道一个性质:对于一个完全平方数,其分解质因数后每个质因数的次幂为偶数,所以我们将 1 1 1 n n n 的数分解质因数后,将质因数个数为奇数的 − 1 -1 1(相当于去掉了若干个互不相同的质数)。然后就可以得到一个最大的完全平方数。
因为将每个数质因数分解的复杂度比较大,所以我们从质数开刀,记录有多少个数包含他,显然 1 − n 1-n 1n 中有 n ÷ p r i m e j n \div prime_j n÷primej 个数有 p r i m e j prime_j primej 这个质数,包含 2 2 2 次的为 n ÷ p r i m e j ÷ p r i m e j n \div prime_j \div prime_j n÷primej÷primej 个。这个可以用肥肠煎蛋的分配律来考虑。接下来放上代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5000007;
const int pyz=1e8+7;
int n,m;
inline int read()
{
    char c;
    int res,flag=0;
    while((c=getchar())>'9'||c<'0') if(c=='-')flag=1;
    res=c-'0';
    while((c=getchar())>='0'&&c<='9') res=(res<<3)+(res<<1)+c-'0';
    return flag?-res:res;
}
bool is[N];
int ans,tmp,cnt[N],prime[N];
inline int ksm(int s,int t)
{
    int res=1;
    while(t)
    {
        if(t&1) res=(ll)res*s%pyz;
        s=(ll)s*s%pyz;
        t>>=1;
    }
    return res;
}
int main()
{
    n=read();
    for(int i=2;i<=n;++i)
    {
        if(!is[i]) prime[++prime[0]]=i;
        for(int j=1;j<=prime[0]&&i*prime[j]<=n;++j)
        {
            is[i*prime[j]]=1;
            if(!(i%prime[j])) 
				break;
        }
    }
    for(int i=1;i<=prime[0];++i)
    {
        tmp=n;
        while(tmp/prime[i]>0)
        {
            cnt[i]+=tmp/prime[i];
            tmp/=prime[i];
        }
    }
    ans=1;
    for(int i=1;i<=prime[0];++i)
    {
        if(cnt[i]&1) cnt[i]--;
		ans=(ll)ans*ksm(prime[i],cnt[i])%pyz;
    }
    printf("%d",ans);
}

寒风农场

这道题目的难点就在于读题目,我们不妨来简化一下题面, a + ( a   m o d   n ) + ( ( a + ( a   m o d   n ) )   m o d   n ) + … a+(a \bmod n)+((a+(a \bmod n)) \bmod n)+\dots a+(amodn)+((a+(amodn))modn)+ 什么时候是 n n n 的倍数,我们再化简一下 ( a + ( a   m o d   n ) + ( ( a + ( a   m o d   n ) )   m o d   n ) + …   )   m o d   n (a+(a \bmod n)+((a+(a \bmod n)) \bmod n)+ \dots ) \bmod n (a+(amodn)+((a+(amodn))modn)+)modn 这个式子什么时候等于 0 0 0 。再再化简一下,可以得到 ( a + a 2 + a 4 + a 8 + …   )   m o d   n (a+a^2+a^4+a^8+ \dots ) \bmod n (a+a2+a4+a8+)modn 这个式子什么时候等于 0 0 0 。接下来就是皆大欢喜的暴力了。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int T,a,n;
bool check(int x)
{
	int k=1;
	if(x==0)
		return false;
	while(k<=x)
	{
		if(k==x)
			return true;
		k*=2;
	}
	return false;
}
signed main()
{
	cin>>T;
	for(int i=1;i<=T;i++)
	{
		scanf("%lld%lld",&a,&n);
		bool flag=true;
		for(int j=1;j<=30;j++)
		{
			if(a%n==0)
			{
				cout<<"Yes"<<endl;
				flag=false;
				break;
			}
			a*=2;
		}
		if(flag)
			cout<<"No"<<endl;
	}
}
CQOI余数之和

首先,简化题意是必不可少的,那么这一题求的就是 a n s = ∑ i = 1 n k % i ans=\sum_{i=1}^{n}k \%i ans=i=1nk%i。那么搞清楚这一点之后,我们又知道高精取模也就是 a m o d    b = a − b × ⌊ a b ⌋ a \mod b=a-b \times \left \lfloor \frac{a}{b} \right \rfloor amodb=ab×ba,所以 a n s = ∑ i = 1 n k − i × ⌊ k i ⌋ = n k − ∑ i = 1 n i × ⌊ k i ⌋ ans=\sum_{i=1}^{n}k-i \times \left \lfloor \frac{k}{i} \right \rfloor=nk-\sum_{i=1}^{n}i \times \left \lfloor \frac{k}{i} \right \rfloor ans=i=1nki×ik=nki=1ni×ik。根据模数据,我们可以知道,这道题又要用到数论分块了,所以我们从 ⌊ k i ⌋ \left \lfloor \frac{k}{i} \right \rfloor ik 出发做分块, ⌊ k i ⌋ \left \lfloor \frac{k}{i} \right \rfloor ik 大约有 k \sqrt{k} k 种取值所以时间复杂度 O ( k ) O(\sqrt{k}) O(k )。上代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k;
int solve2(int l,int r) 
{ 
	return((l+r)*(r-l+1))>>1; 
}
int solve() 
{
    int l=1,r=0,res=0;
    while(l<=n) 
	{
        if(k/l) 
			r=min(n,k/(k/l)); 
		else 
			r=n;
        res+=solve2(l,r)*(k/l);
        l=r+1;
    }
    return res;
}
signed main() 
{
    cin>>n>>k;
    cout<<(n*k-solve())<<endl;
}
5的倍数的个数

一个长度为 n n n 的数字串 s s s,有多少种方法可以使得删除若干个数字后剩下的数字串构成的数字能被 5 5 5​ 整除。可以有前导零,但删完后的数字串不能为空。


这道题我们需要先考虑单独一个串的情况,假设第 i i i 位上的数字为 0 0 0 或者是 5 5 5 ,就说明了只要把 [ i + 1 , n ] \left[i+1,n\right] [i+1,n] 全部删掉就满足条件了。 [ 1 , i + 1 ] \left[1,i+1\right] [1,i+1] 这段里面的数非常随意,每一个会对答案产生 2 i − 1 2^{i-1} 2i1 的贡献。然后再考虑 k k k s s s 的情况,假设单独一个串 s s s 对答案的总贡献为 a n s ans ans ,则 s s ss ss 就会对答案产生 a n s + 2 n × a n s ans+2n×ans ans+2n×ans 的贡献,其中 n n n 表示 s s s 的长度,相当于把第一个 s s s 中的每个数字都删或不删,总共就是 2 n 2n 2n 次方种情况,再用乘法原理乘上第二个 s s s 中的情况,然后我们就得到了一个漂亮的等比数列,然后求和,用上快速幂,FM小定理求一下逆元就完成了,上代码:

#include<bits/stdc++.h>
using namespace std;
const int mod=1000000007;
#define int long long 
long long qp(long long a, long long b,long long c)
{
	if(b==0) 
		return 1;
	if(b==1) 
		return a%c;
	long long t=qp(a,b/2,c)%c;
	return (((t*t)%c)*(b%2==0?1:a%c))%c;
}
signed main(){
    string s;
    int ans=0;
    int n,k;
    cin>>s;
    scanf("%lld",&k);
    n=s.size();
    for(int i=0;i<n;i++)
	{
        if(s[i]-'0'==0||s[i]-'0'==5)
		{
            int a=i;
            int fenzi=(qp(2,k*n+a,mod)-qp(2,a,mod)+mod)%mod;
            int fenmu=qp((qp(2,n,mod)-1+mod)%mod,mod-2,mod);
            ans+=fenzi*fenmu%mod;
        }
    }
    printf("%lld\n",ans%mod);
    return 0;
}
  • 26
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值