数论考试题解
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
",C(k,n)%2);
}
return 0;
}
T2远征前的游戏
经过样例解释加上手动打表模拟,我们能够发现如下规律:原先序号为 (x) 的牌,经过一次变换后,它的位置是 (2 imes x mod (n+1))
那么经过 (m) 次变换后,这张牌的位置就是 (2^m imes x equiv L ( mod(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)) 互质的对数
于是,我们不妨设 (xleq 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
",ans);
return 0;
}