数论试题题解
数论考试题解
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;
}