数论试题题解

数论考试题解

T1致远星的礼物

  • 本题解法较多,此处讲述三种

法一:

  • 定理:对于 \(C_n^k\) , 若 \(n\ \& \ k == k\) , 则 \(C_n^k\) 为奇数,否则,其为偶数

  • 证明:数学归纳法
    实际上,对于一个组合数 \(C_n^k\),如果用二进制来表示 \(n\)\(k\) ,那么如果 \(n\ \& \ k == k\)\(n-k\)\(k\) 中1的个数和刚好就是 \(n\) 的二进制表示中一的个数和,显然,其为奇数
    反之,其为偶数

法二:

  • 利用唯一分解定理,将组合数 \(C_n^k = n!/((n-m)!*(m!))\) 中所有的2分解出来,若最终2的个数为0,则为奇数,否则,为偶数

法三:

  • Lucas定理
  #include<iostream>
#include<cstdio>
#include<math.h>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;

long long m,n,k;

ll C(ll x,ll y)
{
    if(y==x)
        return 1;
    if(y==0)
        return 0;
    if(x==0)
        return 1;
    return C(x/2,y/2)*C(x%2,y%2);//Lucas定理
}

int main(void)
{
	scanf("%lld",&m);
	
	while(m--)
	{
		scanf("%lld%lld",&n,&k);
		printf("%lld\n",C(k,n)%2);
	}
	
	return 0;
} 

T2远征前的游戏

经过样例解释加上手动打表模拟,我们能够发现如下规律:原先序号为 \(x\) 的牌,经过一次变换后,它的位置是 \(2\times x \bmod (n+1)\)

那么经过 \(m\) 次变换后,这张牌的位置就是 \(2^m \times x \equiv L ( \bmod(n+1))\)

通过求逆元,我们可以很容易地求解出该方程的解

#include<iostream>
#include<cstdio>
#include<math.h>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;

ll n,m,k,x,y,ans;

inline ll exgcd(ll a,ll b,ll &x,ll &y)
{
	if(b==0)
	{
		x=1,y=0;
		return a;
	}
	ll d=exgcd(b,a%b,x,y);
	ll z=x;
	x=y;
	y=z-y*(a/b);
	
	return d;
}

inline ll mul(ll a,ll b,ll p)
{
	ll ans=0;
	while(b)
	{
		if(b&1) ans=(ans+a)%p;
		a=(a+a)%p;
		b>>=1;
	}
	
	return ans%p;
}

inline ll ksm(ll a,ll b,ll p)
{
	ll ans=1;
	while(b)
	{
		if(b&1) ans=mul(ans,a,p);
		a=mul(a,a,p);
		b>>=1;
	}
	
	return ans%p;
}

int main(void)
{
	scanf("%lld%lld%lld",&n,&m,&k);
	
	exgcd(2,n+1,x,y);
	
	x=(x%(n+1)+n+1)%(n+1);
	
	x=ksm(x,m,n+1);
	
	ans=mul(k,x,n+1);
	
	printf("%lld",ans);
	
	return 0;
}

T3战舰的钥匙

\(\sum_{i=1}^{n}\sum_{j=1}^{n} gcd(i,j)\) 为素数,必然会枚举到小于等于 \(n\) 的所有素数

那么,对于每一个素数 \(p_i\) ,只需要求出在区间 \([1,{n/p_i}]\) 中,满足有序数对 \((x,y)\) 互质的对数

于是,我们不妨设 \(x\leq y\),那么对于枚举到的一个数 \(y\),求在小于 \(y\) 的数中与 \(y\) 互质的数的个数,即为求 \(\varphi{(y)}\) 的数值

那么,我们可以用线性筛预处理出欧拉函数的值,然后再将答案乘 \(2\)\(x > y\)\(x < y\) 的情况),最后再把 \(x = y\) 的情况加上即可

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=10000010;
long long n,phi[maxn],pri[maxn],presum[maxn],cnt=1;
long long ans=0;

int main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;i++){
        phi[i]=i;
    }
    for(int i=2;i<=n;i++){
        if(phi[i]==i){
            pri[cnt++]=i;
            for(int j=i;j<=n;j+=i){
                phi[j]=phi[j]/i*(i-1);
            }
        }
    }
    for(int i=1;i<=n;i++){
        presum[i]=presum[i-1]+phi[i];
    }
    for(int i=1;i<cnt;i++){
        ans+=presum[n/pri[i]]*2-1;
    }
    printf("%lld\n",ans);
    return 0;
}
posted @ 2020-07-31 14:24  雾隐  阅读(151)  评论(0编辑  收藏  举报