数学专题第22组题解

传送门
密码:202201150000

A - A^B Mod C

思路:快速幂

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll qm(ll a,ll b,ll mod)
{
    ll res=1;
    a%=mod;//a不能太大 
    while(b){
        if(b&1)//如果b为奇数
            res=(res*a)%mod;
        a=(a*a)%mod;
        b>>=1;//位运算每轮b除二 
    }
    return res;
}
int main()
{
	ll a,b,mod,res;
	cin>>a>>b>>mod;
	res=qm(a,b,mod);
	cout<<res;
	return 0;
}

B - 逆元

思路:输出前(p-1)个数的逆元,但用常规方法会超时,但我们发现前(p-1)个数的逆元中,1~p-1各出现了一遍,于是等差数列求和得出了答案,当然i和p不互质就不存在逆元,只要p是素数就能和前面每一个数互质

要观察规律也得是知道怎么求一个数的逆元,下面我介绍三种方法:

1:扩展欧几里得算法(o(logmod),适用于所有模)
 ll exgcd(ll a,ll b,ll &x,ll &y){
	if(b==0)
	{
		x=1,y=0;
		return a;
	}
	ll ret=exgcd(b,a%b,y,x);
	y-=a/b*x;
	return ret;
}
ll inv(int a,int mod){
	ll x,y;
	ll d=exgcd(a,mod,x,y);
	return d==1?(x%mod+mod)%mod:-1;
}

—————————————————————————我是一条可爱的分割线———————————————————————————————————
    
2:费马小定理/欧拉定理(o(logmod),就是求a^mod-2,只适用于mod为素数)
ll qkpow(ll a,ll p,ll mod){
	ll t=1,tt=a%mod;
	while(p)
	{
		if(p&1)t=t*tt%mod;
		tt=tt*tt%mod;
		p>>=1;
	}
	return t;
}
ll inv(ll a,ll mod)
{
	return qkpow(a,mod-2,mod);
}

—————————————————————————我是一条可爱的分割线———————————————————————————————————
    
3:递归求逆元(o(logmod),思路和线性求逆元差不多,记住公式就完了,同样只适用于mod为素数)
ll inv(ll a,ll m)
{
    return a==1?1:(m-m/a)*inv(m%a,m)%m;
}

—————————————————————————我是一条可爱的分割线———————————————————————————————————
    
int main()
{
    ll a,ret;
    scanf("%lld",&a);
    	for(int i=1;i<a;i++){
    		ret=inv(i,a);
    		printf("%lld ",ret);
		}
    return 0;
}

题解:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
bool isprime(ll x){//p为1时没请求,这里不需要考虑1
    for(ll i=2;i*i<=x;i++){
        if(x%i==0)
            return false;
    }
    return true;
}
int main()
{
    ll p;
    while(cin>>p){
        if(!isprime(p)){
            cout<<"AKCniubi"<<endl;
        }else{
            cout<<(p-1)*p/2<<endl;
        }
    }
    return 0;
}

C - 判决素数个数

#include<bits/stdc++.h>
using namespace std;
bool isprime(int x){
	if(x==1) 
	return false;
    for(int i=2;i*i<=x;i++){
        if(x%i==0)
            return false;
    }
    return true;
}
int main()
{
	int a,b,i,t,sum=0;
	scanf("%d %d",&a,&b);
	if(a>b){//题目并没有说a和b的大小关系
		t=a;
		a=b;
		b=t;
	}
	for(i=a;i<=b;i++){
		if(isprime(i)){
			sum++;
		}
	}
	printf("%d",sum);
	return 0;
}

D - 矩阵乘法

虽然咱新生还没开线性代数,但这道题咱们已经遇到不止一次了

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int a[110][110],b[110][110],c[110][110];
    int m,p,n;
    int i,j,k;
    scanf("%d%d%d",&m,&p,&n);
    for(i=0;i<m;i++)
        for(j=0;j<p;j++)
            scanf("%d",&a[i][j]);
    for(i=0;i<p;i++)
        for(j=0;j<n;j++)
            scanf("%d",&b[i][j]);
    for(i=0;i<m;i++)//核心
        for(j=0;j<n;j++){
            c[i][j]=0;
            for(k=0;k<p;k++){
                c[i][j]+=a[i][k]*b[k][j];
            }
        }
    for(i=0;i<m;i++){
        for(j=0;j<n;j++){
            printf("%d ",c[i][j]);
        }
        printf("\n");
    }
    return 0;
}

E - Bash游戏

思路:博弈论,还剩(k+1)个石子时,先手怎么拿都是输

#include<stdio.h>
int main()
{
	int t,n,k;
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&k);
		if(n%(k+1)==0)
		printf("B\n");
		else
		printf("A\n");
	}
	return 0;
}

F - 取石子游戏

思路:威佐夫博奕(讲的可清楚)

先手必输局势(a,b)近似满足(b-a)*1.618=a,因此只需判断两堆石子差值是否满足公式

floor是向下取整,如floor(3.9)=3,0.618是黄金比例,即(sqrt(5)-1.0)/2.0

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int a,b,k,t;
    while(scanf("%d%d",&a,&b)!=EOF){
        if(a>b){
            t=b;
            b=a;
            a=t;
        }
        k=b-a;
        t=(floor)(k*(1.0+sqrt(5))/2.0);//核心
        if(t==a)
        printf("0\n");
        else
        printf("1\n");
        }
    return 0;
}

G - Matches Game

思路:尼姆博弈

取每一堆中火柴的情况可分为四种:

(1)如果轮到某人取火柴的时候,火柴已经没有了,那么此人输,设为P-格局

(2)如果轮到某人取火柴的时候,他能够取完火柴,那么此人赢,设为N-格局

(3)如果轮到某人取火柴的时候,他能够将当前格局转化为某个P格局,那么此格局为N-格局

(4)如果轮到某人取火柴的时候,他不能将当前格局转化为某个P格局,那么此格局为P-格局

有这样一个定理:

一个格局记为(x1,x2,…,xn),且次序无关,此格局为 P-格局 当且仅当 x1 ^ x2 ^ … ^ xn = 0

其中^是按位异或运算,即二进制中两个位相同为0,相异为1,我们只用判断最终状态

因此不断异或,如果结果不为0,则先手胜

#include<stdio.h>
int main()
{
	int n,ret;
	int a[30];
	while(scanf("%d",&n)!=EOF){
		ret=0;
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			ret=ret^a[i];//核心
		}
		if(ret!=0)
		printf("Yes\n");
		else
		printf("No\n");
	}
	return 0;
 }

H - 互质数的个数(一)

思路:欧拉函数

pi是x分解成的质因数,如20=2 * 2 * 5,φ(20)=20*(1-1/2)(1-1/5)=8

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll oula(ll n){
    ll rea=n;
    for(ll i=2;i*i<=n;i++)
        if(n%i==0){
            rea=rea-rea/i;//遇到质因数就乘(1-1/pi)
            do
            n/=i;
            while(n%i==0);//将重复的质因数筛去
        }
    if(n>1)
    rea=rea-rea/n;//对1特殊处理
    return rea;
}
int main()
{
    ll t,ret;
    scanf("%lld",&t);
    while(t--){
        ll v;
        scanf("%lld",&v);
        ret=oula(v);
        printf("%lld\n",ret);
    }
    return 0;
}

I - Sumdiv

思路:逆元+快速幂+约数和定理

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll a,b,m;
ll prime[500000],c[500000];
void divide(ll n){
	for (ll i=2;i<=n/i;i++){
		if (n % i==0){
			prime[++m]=i;
			while(n % i==0){
				n/=i;
				c[m]++;
			}
		}
	}
	if (n>1){
		prime[++m]=n;
		c[m]=1;
	}
}
ll pow(ll a,ll b,ll p){
	ll res=1;
	while(b){
		if(b&1){
			res=res*a%p;
		}
		a=a*a%p;
		b>>=1;
	}
	return res;
}
int main()
{
	cin>>a>>b;
	ll ans=1;
	divide(a);
	for(ll i=1;i<=m;i++){
		if((prime[i]-1)%9901==0){
			ans=(b%9901*c[i]%9901+1)*ans%9901;
			continue;
		}
		ans=(pow(prime[i],b*c[i]+1,9901)-1+9901)%9901*ans%9901;
		ll y=pow(prime[i]-1,9901-2,9901);
		ans*=y;
		ans%=9901;
	}
	printf("%lld",ans);
	return 0;
}

J - The Lottery

思路:容斥原理

我一开始想用素数筛,没看到2^31,数组开大直接编译错误

搬运的题解:

#include<bits/stdc++.h>
using namespace std;
#define EPS 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LL long long
const int MOD = 1E9+7;
const int N = 2000+5;
const int dx[] = {0,0,-1,1,-1,-1,1,1};
const int dy[] = {-1,1,0,0,-1,1,-1,1};
using namespace std;
LL a[N];
LL GCD(LL a,LL b) {
    return b==0?a:GCD(b,a%b);
}
LL LCM(LL a,LL b){
    return a/GCD(a,b)*b;
}
int main()
{
    LL n,m;
    while(scanf("%lld%lld",&n,&m)!=EOF&&(n+m)) {
        int tot=0;
        for(LL i=0; i<m; i++){
            LL val;
            scanf("%lld",&val);
            if(val>0&&val<n)
                a[tot++]=val;
        }
        LL sum=0;
        for(LL i=1; i<(1<<tot); i++) {
            LL lcm=1;
            LL cnt=0;
            for(LL j=0; j<tot; j++) {
                if(i&(1<<j)) {
                    lcm=LCM(lcm,a[j]);
                    cnt++;
                }
            }
            if(cnt!=0){
                if(cnt&1)
                    sum+=(n)/lcm;
                else
                    sum-=(n)/lcm;
            }
        }
        if(sum<0)
            sum=0;
        printf("%lld\n",n-sum);
    }
    return 0;
}

K - 组合数问题

思路:杨辉三角与组合数的关系

#include<bits/stdc++.h>
using namespace std;
int c[2100][2100],f[2100][2100];
int t,k;
int main()
{
    scanf("%d%d",&t,&k);
        for(int i=1;i<=2000;i++){
        	c[i][i]=1;
			c[i][0]=1;
		}
        for(int i=1;i<=2000;i++)
            for(int j=1;j<i;j++)
                c[i][j]=(c[i-1][j-1]+c[i-1][j])%k;
        for(int i=1;i<=2000;i++)
            for(int j=1;j<=2000;j++){
                f[i][j]=f[i][j-1]+f[i-1][j]-f[i-1][j-1];
                if(c[i][j]==0&&j<=i)
                f[i][j]++;
            }
    while(t--){
        int n,m;
        scanf("%d%d",&n,&m);
        printf("%d\n",f[n][m]);
    }
    return 0;
}

L - 同余方程

思路:扩展欧几里得

#include<bits/stdc++.h>
using namespace std;
int a,q;
int x,y;
void exgcd(int a,int b)
{
	if(b==0){//到达边界 
		x=1;
		y=0;
		return;
	}else{
		exgcd(b,a%b);//递归调用倒回去求最小解 
		int k=x;
		x=y;
		y=k-(a/b)*y;
	}
	return;
}
int main()
{
	scanf("%d%d",&a,&q);  
	exgcd(a,q);
	printf("%lld",(x+q)%q);//+q以防x为负数 
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值