Typesetting math: 100%

简单数论与简单博弈论

数论博弈论

上下取整问题

ab=a+b1b(a>0,b>0)ab=a+b1b(a>0,b>0)(1)(2)

证明:

a<b时证明较为容易不在此处提供。

关于ax+by的结论以及证明

1.ax+bycx>0y>0c=ab2.ax+bycx>=0y>=0c=abab(a1)(b1)/2a,ba,b1.ax+bycx>0y>0c=ab2.ax+bycx>=0y>=0c=abab(a1)(b1)/2a,ba,b(3)(4)(5)

这里给出2的证明,1的证明与之类似,后期补充完整。

反证法:假设c=ab-a-b能够被ax+by表示,则ax+by=ab-a-b,则移项合并得a(x+1)+b(y+1)=ab,因为x,y>=0,所以x+1,y+1>=1.

设m=x+1,n=y+1,得m,n>=1,则am+bn=ab,因为ab是a得倍数,am也是a的倍数,即a|am,a|ab,所以想要算式成立,

则需要a|bn,因为a,b互质,所以需要a|n,即n是a的倍数,那么n>a,那么nb>ab,那么am+bn>ab,则假设不成立。

证明了不可以被表示之外还需要证明>ab-a-b的数字一定可以被表示

即需要证明ax+by=ab-a-b+i(i>=1)

因为a,b互质,所以gcd(a,b)=1,则根据裴属定理可得ma+nb=1,假设m>0,n<0,ab-a-b+i(ma+nb) ----> ab-a-b+ima+inb

-----> (im-1)a+(a-1+in)b,根据i>=1,m>0可得im-1>0,只需要再证明出a-1+in>=0即可. ima+inb=i ---> ima=i-inb, 因为i>=1,b>0

n<0,所以inb<0,那么ima=i+|inb|,

必要性证明请参照数论:px+py 不能表示的最大数为pq-p-q的证明 - PIPIBoss - 博客园 (cnblogs.com)

暂时上方的必要性证明没有证明出来,简单查了一下也没看懂

九余数定理

一个数对9取余后的结果成为九余数

一个数每一位上的数字之和相加后的结果<10的数字称为这个数的九余数(如果每一位相加后>10,则继续拆分每一位继续相加)

和的模 等于 模的和再取模 (15+7)%9 = (15%9+7%9)%9

积的模 等于 模的积再取模 (157)%9 = (15%97%9)%9

所以由此可以得到一个数字每一位上的数相加后的结果(<10)等于该数字对9趋于的结果

AnaiA=n1i=0ai10i10n1n1 mod 9An1i=0ai mod 9AnaiA=n1i=0ai10i10n1n1 mod 9An1i=0ai mod 9(6)(7)(8)(9)(10)

秦九韶算法

针对于一元n次多项式的计算的优化

f(x)=anxn+an1xn1++a2x2+a1x+a0=(anxn1+an1xn2++a2x+a1)x+a0=((anxn2+an1xn3++a2)x+a1)x+a0...=(((an+an1)x+an2)x++a1)x+a0f(x)=anxn+an1xn1++a2x2+a1x+a0=(anxn1+an1xn2++a2x+a1)x+a0=((anxn2+an1xn3++a2)x+a1)x+a0...=(((an+an1)x+an2)x++a1)x+a0(11)(12)(13)(14)(15)

质数

质数定义:因子只包含1和其本身的数,称为质数.

一、试除法判定质数

:ad|aad|aa[2,a]..:ad|aad|aa[2,a]..(16)(17)(18)(19)

/*
给定 n 个正整数 ai,判定每个数是否是质数。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一个正整数 ai。
输出格式
共 n 行,其中第 i 行输出第 i 个正整数 ai 是否为质数,是则输出 Yes,否则输出 No。
数据范围
1≤n≤100,
1≤ai≤2^31−1
输入样例:
2
2
6
输出样例:
Yes
No
*/
#include <iostream>
using namespace std;
int n;
bool isprime(int x);

int main()
{
    cin>>n;
    while(n--)
    {
        int a;
        cin>>a;
        if(isprime(a))
            puts("Yes");
        else
            puts("No");
    }
    
    return 0;
}
bool isprime(int x)
{
    if(x<2)
        return false;
    for(int i=2;i<=x/i;++i)//遍历[2,sqrt(x)]区间判断是否有多的约数
    {
        if(x%i==0)
        {
            return false;
        }
    }
    return true;
}

二、分解质因数

:nn.:np1,p2.n>p1p2>(n)2=nn>n.[2,n]n.[2,n].[2,n]123n/(),..:nn.:np1,p2.n>p1p2>(n)2=nn>n.[2,n]n.[2,n].[2,n]123n/(),..(20)(21)(22)(23)(24)(25)(26)(27)(28)(29)

/*
给定 n 个正整数 ai,将每个数分解质因数,并按照质因数从小到大的顺序输出每个质因数的底数和指数。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一个正整数 ai。
输出格式
对于每个正整数 ai,按照从小到大的顺序输出其分解质因数后,每个质因数的底数和指数,每个底数和指数占一行。
每个正整数的质因数全部输出完毕后,输出一个空行。
数据范围
1≤n≤100,
1≤ai≤2×109
输入样例:
2
6
8
输出样例:
2 1
3 1

2 3

*/
#include <iostream>
using namespace std;
void divide(int n);

int main()
{
    int n;
    cin>>n;
    while(n--)
    {
        int a;
        cin>>a;
        divide(a);
    }
    return 0;
}

void divide(int n)
{
    for(int i=2;i<=n/i;++i)
    {
        if(n%i==0)
        {
            int s=0;
            while(n%i==0)//此处保证较小的质数全部除去,不会存在多余质因子构成合数
            {
                n/=i;
                ++s;
            }
            printf("%d %d\n",i,s);
        }
    }
    if(n>1)//判断是否有大于sqrt(n)的质因子存在
        printf("%d %d\n",n,1);
    puts("");
}

三、筛质数(筛法)

/*
给定一个正整数 n,请你求出 1∼n 中质数的个数。
输入格式
共一行,包含整数 n。
输出格式
共一行,包含一个整数,表示 1∼n 中质数的个数。
数据范围
1≤n≤1e6
输入样例:
8
输出样例:
4
*/

①.朴素筛法(O(nlog n))

朴素筛法的主要思想:在遍历的过程中,将其倍数全部筛去,那么剩下的全是质数.

//朴素筛法
#include <iostream>
using namespace std;
const int N=1e6+10;
bool st[N];
int cnt,prime[N];
void get_prime(int n);

int main()
{
    int n;
    scanf("%d",&n);
    get_prime(n);
    printf("%d\n",cnt);
    return 0;
}
void get_prime(int n)
{
    for(int i=2;i<=n;++i)
    {
        if(!st[i])
        {
            prime[cnt++]=i;
        }
        for(int j=i+i;j<=n;j+=i)//将所有数得倍数删去
        {
            st[j]=true;
        }
    }
}

②.埃式筛法(优化版朴素筛法)(O(nloglog n))

埃式筛法的主要思想:将所有质数的倍数删去.

//埃式筛法
#include <iostream>
using namespace std;
const int N=1e6+10;
bool st[N];
int cnt,prime[N];
void get_prime(int n);

int main()
{
    int n;
    scanf("%d",&n);
    get_prime(n);
    printf("%d\n",cnt);
    return 0;
}
void get_prime(int n)
{
    for(int i=2;i<=n;++i)
    {
        if(!st[i])//若是质数则加入prime数组
        {
            prime[cnt++]=i;
            for(int j=i+i;j<=n;j+=i)//将所有质数的倍数筛去
            {
                st[j]=true;
            }
        }
    }
}

③.线性筛(欧拉筛法)(O(n))

线性筛的主要思想:让每个合数仅被最小质因子筛去.

//线性筛法(欧拉筛法)
#include <iostream>
using namespace std;
const int N=1e6+10;
int prime[N],cnt;
bool st[N];
void get_prime(int n);

int main()
{
    int n;
    scanf("%d",&n);
    get_prime(n);
    printf("%d\n",cnt);
    return 0;
}
void get_prime(int n)
{
    for(int i=2;i<=n;++i)
    {
        if(!st[i])
            prime[cnt++]=i;
        for(int j=0;i*prime[j]<=n;++j)
        {
            st[i*prime[j]]=true;//使得合数被最小质因子筛去
            if(i%prime[j]==0)//如果该数字已经被筛则退出循环
                break;
        }
    }
}

约数

①.试除法求约数

(d)a|d,da|d.dd[1,d],.,.(d)a|d,da|d.dd[1,d],.,.(30)(31)(32)(33)

/*
给定 n 个正整数 ai,对于每个整数 ai,请你按照从小到大的顺序输出它的所有约数。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一个整数 ai。
输出格式
输出共 n 行,其中第 i 行输出第 i 个整数 ai 的所有约数。
数据范围
1≤n≤100,
2≤ai≤2×109
输入样例:
2
6
8
输出样例:
1 2 3 6 
1 2 4 8 
*/
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
vector<int> getdivisors(int n);

int main()
{
    int n;
    cin>>n;
    while(n--)
    {
        int x;
        cin>>x;
        auto res=getdivisors(x);
        for(auto t:res)
        {
            cout<<t<<' ';
        }
        puts("");
    }
    return 0;
}
vector<int> getdivisors(int n)
{
    vector<int> res;
    for(int i=1;i<=n/i;++i)
    {
        if(n%i==0)
        {
            res.push_back(i);
            if(i!=n/i)//若另一半约数相同,则只需存放一个
                res.push_back(n/i);
        }
    }
    sort(res.begin(),res.end());//将所有约数排序
    return res;
}

②.约数个数

对于一个大于1正整数n可以分解质因数:(分解质因数方法参考前文)

则n的正约数的个数就是

.:1..pa11a1+1...:1..pa11a1+1..(34)(35)(36)(37)(38)

/*
给定 n 个正整数 ai,请你输出这些数的乘积的约数个数,答案对 109+7 取模。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一个整数 ai。
输出格式
输出一个整数,表示所给正整数的乘积的约数个数,答案需对 109+7 取模。
数据范围
1≤n≤100,
1≤ai≤2×109
输入样例:
3
2
6
8
输出样例:
12
*/
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
typedef long long LL;
const LL mod=1e9+7;

int main()
{
    int n;
    cin>>n;
    unordered_map<int,int> primes;
    while(n--)
    {
        int x;
        cin>>x;
        for(int i=2;i<=x/i;++i)
        {
            while(x%i==0)
            {
                primes[i]++;
                x/=i;
            }
        }
        if(x>1)//所有质因子分解完判断是否存在剩余的最后一个>sqrt(x)的因子
        {
            primes[x]++;
        }
    }
    LL res=1;
    for(auto t:primes)
    {
        res=res*(t.second+1)%mod;//根据公式在计算过程中取模以防溢出
    }
    cout<<res<<endl;
    return 0;
}

③.约数之和

f(n)=(p01+p11+p21+pa11)(p02+p12+p22+pa22)(p0k+p1k+p2k+pkak)f(n)=(p01+p11+p21+pa11)(p02+p12+p22+pa22)(p0k+p1k+p2k+pkak)

.使..使.(39)(40)(41)

/*
给定 n 个正整数 ai,请你输出这些数的乘积的约数之和,答案对 109+7 取模。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一个整数 ai。
输出格式
输出一个整数,表示所给正整数的乘积的约数之和,答案需对 109+7 取模。
数据范围
1≤n≤100,
1≤ai≤2×109
输入样例:
3
2
6
8
输出样例:
252
*/
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
typedef long long LL;
const LL mod=1e9+7;

int main()
{
    int n;
    cin>>n;
    unordered_map<int,int> primes;
    while(n--)//质因子分解并存储
    {
        int x;
        cin>>x;
        for(int i=2;i<=x/i;++i)
        {
            while(x%i==0)
            {
                primes[i]++;
                x/=i;
            }
        }
        if(x>1)
            primes[x]++;
    }
    LL res=1;
    for(auto t:primes)
    {
        LL p=t.first;
        LL q=t.second;
        LL tmp=1;
        while(q--)
        {
            tmp=(tmp*p+1)%mod;//秦九韶算法
        }
        res=res*tmp%mod;
    }
    cout<<res<<endl;
    return 0;
}

④.最大公约数(欧几里得算法(辗转相除法))

gcd(a,b)=gcd(b,a%b):d|a,d|b;d=gcd(a,b).d|a,d|bd|(a+b).a%b=a(a/b)b:a/b.d|(a(a/b)b).gcd(a,b)=gcd(b,a%b):d|a,d|b;d=gcd(a,b).d|a,d|bd|(a+b).a%b=a(a/b)b:a/b.d|(a(a/b)b).(42)(43)(44)(45)(46)

/*
给定 n 对正整数 ai,bi,请你求出每对数的最大公约数。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一个整数对 ai,bi。
输出格式
输出共 n 行,每行输出一个整数对的最大公约数。
数据范围
1≤n≤105,
1≤ai,bi≤2×109
输入样例:
2
3 6
4 6
输出样例:
3
2
*/
#include <iostream>
#include <algorithm>
using namespace std;
int gcd(int a,int b);

int main()
{
    int n;
    cin>>n;
    while(n--)
    {
        int a,b;
        cin>>a>>b;
        cout<<gcd(a,b)<<endl;
        //cout<<__gcd(a,b)<<endl;//这里是两个下划线
    }
    return 0;
}
int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;
    /*
    if(a<b)
        swap(a,b);
    int t;
    while(a%b)
    {
        t=a%b;
        a=b;
        b=t;
    }
    return b;
    */
}

欧拉函数

对于一个大于1正整数n可以分解质因数:(分解质因数方法参考前文)

nf(n)f(1)=1a,bf(ab)=f(a)f(b)f(n)a,bf(ab)=f(a)f(b)gcd(a,b)=1.1NN:φ(n)=n(11p1)(11p2)(11pk):[1,N]N.N=1,1.NNN1Np1Np1p2Np2N,:NNp1Np2+Np1p2+Np1p3+Np1p2p3Np1p2p4,..nf(n)f(1)=1a,bf(ab)=f(a)f(b)f(n)a,bf(ab)=f(a)f(b)gcd(a,b)=1.1NN:φ(n)=n(11p1)(11p2)(11pk):[1,N]N.N=1,1.NNN1Np1Np1p2Np2N,:NNp1Np2+Np1p2+Np1p3+Np1p2p3Np1p2p4,..(47)(48)(49)(50)(51)(52)(53)(54)(55)(56)(57)(58)

/*
给定 n 个正整数 ai,请你求出每个数的欧拉函数。
欧拉函数的定义参照上文.
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一个正整数 ai。
输出格式
输出共 n 行,每行输出一个正整数 ai 的欧拉函数。
数据范围
1≤n≤100,
1≤ai≤2×109
输入样例:
3
3
6
8
输出样例:
2
2
4
*/
#include <iostream>
#include <algorithm>
using namespace std;
int euler(int x);

int main()
{
    int n;
    cin>>n;
    while(n--)
    {
        int x;
        cin>>x;
        cout<<euler(x)<<endl;
    }
    return 0;
}
int euler(int x)
{
    int res=x;//保证答案初始乘上x
    for(int i=2;i<=x/i;++i)//质因子分解的同时计算欧拉函数
    {
        if(x%i==0)
        {
            res=res/i*(i-1);//防止(1-1/i)发生整除
            while(x%i==0)
            {
                x/=i;
            }
        }
    }
    if(x>1)
    {
        res=res/x*(x-1);
    }
    return res;
}

线().phi[N].:pφ(p)=p1.pp==1φ(1)=1p!=1if(p%primes[j]==0)phi[primes[j]p]=phi[p]primes[j],primes[j]pprimes[j]p,primes[j]便phi[peimes[j]p]if(p%primes[j])primes[j]pprimes[j]primes[j]p,φ(prmies[j])=primes[j](11primes[j])=primes[j]1φ(primes[j]p)=phi[p](primes[j]1).便线.线().phi[N].:pφ(p)=p1.pp==1φ(1)=1p!=1if(p%primes[j]==0)phi[primes[j]p]=phi[p]primes[j],primes[j]pprimes[j]p,primes[j]便phi[peimes[j]p]if(p%primes[j])primes[j]pprimes[j]primes[j]p,φ(prmies[j])=primes[j](11primes[j])=primes[j]1φ(primes[j]p)=phi[p](primes[j]1).便线.(59)(60)(61)(62)(63)(64)(65)

/*
给定一个正整数 n,求 1∼n 中每个数的欧拉函数之和。
输入格式
共一行,包含一个整数 n。
输出格式
共一行,包含一个整数,表示 1∼n 中每个数的欧拉函数之和。
数据范围
1≤n≤106
输入样例:
6
输出样例:
12
*/
#include <iostream>
#include <algorithm>
using namespace std;
const int N=1e6+10;
typedef long long LL;
int primes[N],idx,phi[N];
bool st[N];
void get_euler(int n);

int main()
{
    int n;
    cin>>n;
    get_euler(n);
    LL res=0;
    for(int i=1;i<=n;++i)
    {
        res+=phi[i];
    }
    cout<<res<<endl;
    return 0;
}

void get_euler(int n)
{
    phi[1]=1;
    for(int i=2;i<=n;++i)
    {
        if(!st[i])
        {
            primes[idx++]=i;
            phi[i]=i-1;
        }
        for(int j=0;primes[j]<=n/i;++j)
        {
            st[primes[j]*i]=true;
            if(i%primes[j]==0)
            {
                phi[primes[j]*i]=primes[j]*phi[i];
                break;
            }
            phi[primes[j]*i]=phi[i]*(primes[j]-1);
        }
    }
}

快速幂

O(logk)ak mod p.aka2x1a2x2a2xi.a2xk1ax.a20a21a22a2logkk1axk1.O(logk)ak mod p.aka2x1a2x2a2xi.a2xk1ax.a20a21a22a2logkk1axk1.(66)(67)(68)(69)(70)

/*
给定 n 组 ai,bi,pi,对于每组数据,求出 ai^bi mod pi 的值。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含三个整数 ai,bi,pi。
输出格式
对于每组数据,输出一个结果,表示 ai^bi mod pi 的值。
每个结果占一行。
数据范围
1≤n≤100000,
1≤ai,bi,pi≤2×109
输入样例:
2
3 2 5
4 3 9
输出样例:
4
1
*/
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
LL qmi(int a,int b,int p);

int main()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        int a,b,p;
        scanf("%d%d%d",&a,&b,&p);
        printf("%d\n",qmi(a,b,p));
    }
    return 0;
}
LL qmi(int a,int b,int p)
{
    LL res=1;
    while(b)
    {
        if(b&1)
        {
            res=res*a%p;
        }
        a=a*(LL)a%p;
        b>>=1;
    }
    return res;
}

:bmab|ax使a/ba×x(mod m)xbmb1(mod m).bbm.mbm2b.amax1(mod m)xam.mam2mod m.:ap.:bmab|ax使a/ba×x(mod m)xbmb1(mod m).bbm.mbm2b.amax1(mod m)xam.mam2mod m.:ap.(71)(72)(73)(74)(75)(76)(77)

/*
给定 n 组 ai,pi,其中 pi 是质数,求 ai 模 pi 的乘法逆元,若逆元不存在则输出 impossible。
注意:请返回在 0∼p−1 之间的逆元。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一个数组 ai,pi,数据保证 pi 是质数。
输出格式
输出共 n 行,每组数据输出一个结果,每个结果占一行。
若 ai 模 pi 的乘法逆元存在,则输出一个整数,表示逆元,否则输出 impossible。
数据范围
1≤n≤105,
1≤ai,pi≤2∗109
输入样例:
3
4 3
8 5
6 3
输出样例:
1
2
impossible
*/
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
LL qmi(int a,int b,int p);

int main()
{
    int n;
    cin>>n;
    while(n--)
    {
        int a,p;
        cin>>a>>p;
        if(a%p==0)
            puts("impossible");
        else
            cout<<qmi(a,p-2,p)<<endl;
    }
    return 0;
}
LL qmi(int a,int b,int p)
{
    LL res=1;
    while(b)
    {
        if(b&1)
            res=res*a%p;
        a=a*(LL)a%p;
        b>>=1;
    }
    return res;
}

拓展欧几里得算法

:abdxy线.:abgcd(a,b)=d,xyax+by=kgcd(a,b)k.xy使ax+by=gcd(a,b)abax+by=1.gcd(a,b)ax+by=gcd(a,b)xy.:abdxy线.:abgcd(a,b)=d,xyax+by=kgcd(a,b)k.xy使ax+by=gcd(a,b)abax+by=1.gcd(a,b)ax+by=gcd(a,b)xy.(78)(79)(80)(81)(82)

/*
给定 n 对正整数 ai,bi,对于每对数,求出一组 xi,yi,使其满足 ai*xi+bi*yi=gcd(ai,bi)。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含两个整数 ai,bi。
输出格式
输出共 n 行,对于每组 ai,bi,求出一组满足条件的 xi,yi,每组结果占一行。
本题答案不唯一,输出任意满足条件的 xi,yi 均可。
数据范围
1≤n≤105,
1≤ai,bi≤2×109
输入样例:
2
4 6
8 18
输出样例:
-1 1
-2 1
*/
#include <iostream>
using namespace std;
int exgcd(int a,int b,int &x,int &y);

int main()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        int a,b,x,y;
        scanf("%d%d",&a,&b);
        exgcd(a,b,x,y);
        printf("%d %d\n",x,y);
    }
    return 0;
}
int exgcd(int a,int b,int &x,int &y)
{
    if(b==0)
    {
        x=1,y=0;//b的系数为0,a的系数为1.
        return a;
    }
    else
    {
        int d=exgcd(b,a%b,y,x);//传入(b,a%b,y,x)方便推到计算
        y-=a/b*x;
        return d;
    }
}

线:线ax b(mod m).: axaxmm=b ax=ym+baxym=by=y ax+my=b.: gcd(a,m)|bbgcd(a,m).线:线ax b(mod m).: axaxmm=b ax=ym+baxym=by=y ax+my=b.: gcd(a,m)|bbgcd(a,m).(83)(84)(85)(86)(87)(88)

/*
给定 n 组数据 ai,bi,mi,对于每组数求出一个 xi,使其满足 ai*xi≡bi(mod mi),如果无解则输出 impossible。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一组数据 ai,bi,mi。
输出格式
输出共 n 行,每组数据输出一个整数表示一个满足条件的 xi,如果无解则输出 impossible。
每组数据结果占一行,结果可能不唯一,输出任意一个满足条件的结果均可。
输出答案必须在 int 范围之内。
数据范围
1≤n≤105,
1≤ai,bi,mi≤2×109
输入样例:
2
2 3 6
4 3 5
输出样例:
impossible
-3
*/
#include <iostream>
using namespace std;
typedef long long LL;
int exgcd(int a,int b,int &x,int &y);

int main()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        int a,b,m;
        int x,y;
        scanf("%d%d%d",&a,&b,&m);
        int d=exgcd(a,m,x,y);
        if(b%d)
            puts("impossible");
        else
            printf("%d\n",(LL)x*b/d %m);//扩大b/d倍并且模到0-m的范围内
    }
    return 0;
}
int exgcd(int a,int b,int &x,int &y)
{
    if(b==0)
    {
        x=1,y=0;
        return a;
    }
    else
    {
        int d=exgcd(b,a%b,y,x);
        y-=a/b*x;
        return d;
    }
}

中国剩余定理

:m1,m2,m3,,mn1a1,a2,a3,,an.::m1,m2,m3,,mn1a1,a2,a3,,an.:(89)(90)

{x a1(mod m1),x a2(mod m2),x a3(mod m3),...x an(mod mn)⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪x a1(mod m1),x a2(mod m2),x a3(mod m3),...x an(mod mn)(91)

M=m1m2m3mn .Mi=Mmi(0in)Mimigcd(Mi,mi)=1便yiMimi使Miyi  1(mod mi).:x=a1M1y1+a2M2y2++anMnyn.M=m1m2m3mn .Mi=Mmi(0in)Mimigcd(Mi,mi)=1便yiMimi使Miyi  1(mod mi).:x=a1M1y1+a2M2y2++anMnyn.(92)(93)(94)(95)

m1,m2,,mn.m1,m2,,mn.(96)(97)

/*
给定 2n 个整数 a1,a2,…,an 和 m1,m2,…,mn,求一个最小的非负整数 x,满足 ∀i∈[1,n],x≡mi(mod ai)。
输入格式
第 1 行包含整数 n。
第 2…n+1 行:每 i+1 行包含两个整数 ai 和 mi,数之间用空格隔开。
输出格式
输出最小非负整数 x,如果 x 不存在,则输出 −1。
如果存在 x,则数据保证 x 一定在 64 位整数范围内。
数据范围
1≤ai≤231−1,
0≤mi<ai
1≤n≤25
输入样例:
2
8 7
11 9
输出样例:
31
*/
#include <iostream>
#include <cmath>
using namespace std;
typedef long long LL;
LL exgcd(LL a,LL b,LL &x,LL &y);

int main()
{
    int n;
    cin>>n;
    LL a1,m1;
    cin>>a1>>m1;
    bool flag=true;
    for(int i=0;i<n-1;++i)
    {
        LL a2,m2;
        cin>>a2>>m2;
        LL k1,k2;
        LL d=exgcd(a1,a2,k1,k2);
        if((m2-m1)%d)
        {
            flag=false;
            break;
        }
        k1*=((m2-m1)/d);
        LL t=a2/d;
        k1=(k1%t+t)%t;
        m1=a1*k1+m1;
        a1=abs(a1/d*a2);
        //a1=abs(a1*a2/d);
        
    }
    if(flag)
        cout<<(m1%a1+a1)%a1<<endl;
    else
        puts("-1");
    return 0;
}

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

高斯消元

线线:线线:(98)

{a11x1+a12x2++a1nxn=b1,a21x1+a22x2++a2nxn=b2,a31x1+a32x2++a3nxn=b3,...an1x1+an2x2++annxn=bn⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪a11x1+a12x2++a1nxn=b1,a21x1+a22x2++a2nxn=b2,a31x1+a32x2++a3nxn=b3,...an1x1+an2x2++annxn=bn(99)

:...()()线.=00.....使1..().:...()()线.=00.....使1..().(100)(101)(102)(103)(104)(105)(106)(107)(108)(109)(110)(111)(112)(113)(114)

/*
输入一个包含 n 个方程 n 个未知数的线性方程组。
方程组中的系数为实数。
求解这个方程组。(参考上方方程组)
输入格式
第一行包含整数 n。
接下来 n 行,每行包含 n+1 个实数,表示一个方程的 n 个系数以及等号右侧的常数。
输出格式
如果给定线性方程组存在唯一解,则输出共 n 行,其中第 i 行输出第 i 个未知数的解,结果保留两位小数。
如果给定线性方程组存在无数解,则输出 Infinite group solutions。
如果给定线性方程组无解,则输出 No solution。
数据范围
1≤n≤100,
所有输入系数以及常数均保留两位小数,绝对值均不超过 100。
输入样例:
3
1.00 2.00 -1.00 -6.00
2.00 1.00 -3.00 -9.00
-1.00 -1.00 2.00 7.00
输出样例:
1.00
-2.00
3.00
*/
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const double eps=1e-6;
const int N=110;
double a[N][N];
int n;
int gauss();

int main()
{
    cin>>n;
    for(int i=0;i<n;++i)
        for(int j=0;j<n+1;++j)
            cin>>a[i][j];
    int t=gauss();
    if(t==0)//返回0代表有唯一的解
    {
        for(int i=0;i<n;++i)
        {
            printf("%.2lf\n",a[i][n]);
        }
    }
    else if(t==1)
        puts("Infinite group solutions");
    else
        puts("No solution");
    return 0;
}

int gauss()
{
    int c,r;
    for(c=0,r=0;c<n;++c)
    {
        int t=r;
        for(int i=r;i<n;++i)
        {
            if(fabs(a[i][c])>fabs(a[r][c]))
                t=i;
        }
        if(fabs(a[t][c])<eps) 
            continue;
        
        for(int i=0;i<n+1;++i)
            swap(a[r][i],a[t][i]);
        
        for(int i=n;i>=0;--i)
            a[r][i]/=a[r][c];
        
        for(int i=r+1;i<n;++i)
        {
            if(fabs(a[i][c])>eps)
            {
                for(int j=n;j>=c;--j)
                {
                    a[i][j]-=(a[r][j]*a[i][c]);
                }
            }
        }
        ++r;
    }
    if(r<n)
    {
        for(int i=r;i<n;++i)
        {
            if(fabs(a[i][n])>eps)
                return 2;
        }
        return 1;
    }
    
    for(int i=n-1;i>=0;--i)
    {
        for(int j=i+1;j<n;++j)
        {
            a[i][n]-=(a[j][n]*a[i][j]);
        }
    }
    
    return 0;
}
/*
输入一个包含 n 个方程 n 个未知数的异或线性方程组。
方程组中的系数和常数为 0 或 1,每个未知数的取值也为 0 或 1。
求解这个方程组。
异或线性方程组示例如下:
M[1][1]x[1] ^ M[1][2]x[2] ^ … ^ M[1][n]x[n] = B[1]
M[2][1]x[1] ^ M[2][2]x[2] ^ … ^ M[2][n]x[n] = B[2]
…
M[n][1]x[1] ^ M[n][2]x[2] ^ … ^ M[n][n]x[n] = B[n]
其中 ^ 表示异或(XOR),M[i][j] 表示第 i 个式子中 x[j] 的系数,B[i] 是第 i 个方程右端的常数,取值均为 0 或 1。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含 n+1 个整数 0 或 1,表示一个方程的 n 个系数以及等号右侧的常数。
输出格式
如果给定线性方程组存在唯一解,则输出共 n 行,其中第 i 行输出第 i 个未知数的解。
如果给定线性方程组存在多组解,则输出 Multiple sets of solutions。
如果给定线性方程组无解,则输出 No solution。
数据范围
1≤n≤100
输入样例:
3
1 1 0 1
0 1 1 0
1 0 0 1
输出样例:
1
0
0
*/
#include <iostream>
#include <algorithm>
using namespace std;
const int N=110;
int a[N][N];
int n;
int gauss();

int main()
{
    cin>>n;
    for(int i=0;i<n;++i)
    {
        for(int j=0;j<n+1;++j)
        {
            cin>>a[i][j];
        }
    }
    
    int t=gauss();
    if(!t)
    {
        for(int i=0;i<n;++i)
            cout<<a[i][n]<<endl;
    }
    else if(t==1)
        puts("Multiple sets of solutions");
    else
        puts("No solution");
    
    return 0;
}

int gauss()
{
    int r,c;
    for(c=0,r=0;c<n;++c)
    {
        int t=r;
        for(int i=r;i<n;++i)
        {
            if(a[i][c])
            {
                t=i;
                break;
            }
        }
        if(!a[t][c])
            continue;
        for(int i=c;i<n+1;++i)
        {
            swap(a[t][i],a[r][i]);
        }
        for(int i=r+1;i<n;++i)
        {
            if(a[i][c])
            {
                for(int j=c;j<n+1;++j)
                {
                    a[i][j]^=a[r][j];
                }
            }
        }
        ++r;
    }
    if(r<n)
    {
        for(int i=r;i<n;++i)
        {
            if(a[i][n])
                return 2;
        }
        return 1;
    }
    
    for(int i=n-1;i>=0;--i)
    {
        for(int j=i+1;j<n;++j)
        {
            a[i][n]^=(a[i][j]&a[j][n]);
        }
    }
    return 0;
}

组合数

Cba=Cba1+Cb1a1Cba=a!b!(ab)!(lucas)Cba=Cb mod pa mod pCb/pa/p(mod p) (O(NlogpN)).:Cba=Cba1+Cb1a1Cba=a!b!(ab)!(lucas)Cba=Cb mod pa mod pCb/pa/p(mod p) (O(NlogpN)).:(115)(116)(117)(118)(119)

.Cba=a!b!(ab)!.pNpNp+Np2+Np3+px>N()Np[1,N]p.pp2p3.便N!p.便..Cba=a!b!(ab)!.pNpNp+Np2+Np3+px>N()Np[1,N]p.pp2p3.便N!p.便.(120)(121)(122)(123)(124)(125)(126)(127)(128)

/*
给定 n 组询问,每组询问给定两个整数 a,b,请你输出 C(a,b)mod(109+7) 的值。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一组 a 和 b。
输出格式
共 n 行,每行输出一个询问的解。
数据范围
1≤n≤10000,
1≤b≤a≤2000
输入样例:
3
3 1
5 3
2 2
输出样例:
3
10
1
*/
#include <iostream>
#include <algorithm>
using namespace std;
const int N=2e3+10,mod=1e9+7;
int c[N][N];
void init();

int main()
{
    init();
    int n;
    scanf("%d",&n);
    while(n--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        printf("%d\n",c[a][b]);
    }
    return 0;
}

void init()
{
    for(int i=0;i<N;++i)
    {
        for(int j=0;j<=i;++j)
        {
            if(!j)
                c[i][j]=1;
            else
                c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
        }
    }
}
/*
给定 n 组询问,每组询问给定两个整数 a,b,请你输出 C(a,b)mod(109+7) 的值。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一组 a 和 b。
输出格式
共 n 行,每行输出一个询问的解。
数据范围
1≤n≤10000,
1≤b≤a≤105
输入样例:
3
3 1
5 3
2 2
输出样例:
3
10
1
*/
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N=1e5+10,mod=1e9+7;
int fact[N],infact[N];
int qmi(int a,int k,int p);
void init();

int main()
{
    init();
    int n;
    scanf("%d",&n);
    while(n--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        printf("%d\n",(LL)fact[a]*infact[b]%mod*infact[a-b]%mod);
    }
    return 0;
}

int qmi(int a,int k,int p)
{
    LL res=1;
    while(k)
    {
        if(k&1)
            res=(LL)res*a%p;
        a=(LL)a*a%p;
        k>>=1;
    }
    return res;
}

void init()
{
    fact[0]=infact[0]=1;
    for(int i=1;i<N;++i)
    {
        fact[i]=(LL)fact[i-1]*i%mod;
        infact[i]=(LL)infact[i-1]*qmi(i,mod-2,mod)%mod;
    }
}
/*
给定 n 组询问,每组询问给定三个整数 a,b,p,其中 p 是质数,请你输出 C(a,b)modp 的值。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一组 a,b,p。
输出格式
共 n 行,每行输出一个询问的解。
数据范围
1≤n≤20,
1≤b≤a≤1018,
1≤p≤105,
输入样例:
3
5 3 7
3 1 5
6 4 13
输出样例:
3
3
2
*/
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
int p;
int qmi(int a,int k);
LL C(int a,int b);
LL lucas(LL a,LL b);

int main()
{
    int n;
    cin>>n;
    while(n--)
    {
        LL a,b;
        cin>>a>>b>>p;
        cout<<lucas(a,b)<<endl;
    }
    return 0;
}

int qmi(int a,int k)
{
    LL res=1;
    while(k)
    {
        if(k&1)
            res=(LL)res*a%p;
        a=(LL)a*a%p;
        k>>=1;
    }
    return res;
}

LL C(int a,int b)
{
    if(b>a)
        return 0;
    LL res=1;
    for(int i=1,j=a;i<=b;++i,--j)
    {
        res=(LL)res*j%p;
        res=res*qmi(i,p-2)%p;
    }
    return res;
}

LL lucas(LL a,LL b)
{
    if(a<p&&b<p)
        return C(a,b);
    return (LL)C(a%p,b%p)*lucas(a/p,b/p)%p;
}
/*
输入 a,b,求 C(a,b) 的值。
注意结果可能很大,需要使用高精度计算。
输入格式
共一行,包含两个整数 a 和 b。
输出格式
共一行,输出 C(a,b) 的值。
数据范围
1≤b≤a≤5000
输入样例:
5 3
输出样例:
10
*/
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int N=5010;
int primes[N],idx,sum[N];
bool st[N];
void get_primes(int n);
int get(int n,int p);
vector<int> mul(vector<int> a,int b);

int main()
{
    int a,b;
    cin>>a>>b;
    get_primes(a);
    
    for(int i=0;i<idx;++i)
    {
        int p=primes[i];
        sum[i]=get(a,p)-get(b,p)-get(a-b,p);
    }
    
    vector<int> res;
    res.push_back(1);
    
    for(int i=0;i<idx;++i)
    {
        for(int j=0;j<sum[i];++j)
        {
            res=mul(res,primes[i]);
        }
    }
    
    for(int i=res.size()-1;i>=0;--i)
    {
        cout<<res[i];
    }
    puts("");
    
    return 0;
}

void get_primes(int n)
{
    for(int i=2;i<=n;++i)
    {
        if(!st[i])
        {
            primes[idx++]=i;
        }
        for(int j=0;primes[j]<=n/i;++j)
        {
            st[primes[j]*i]=true;
            if(i%primes[j]==0)
                break;
        }
    }
}

int get(int n,int p)
{
    int res=0;
    while(n)
    {
        res+=n/p;
        n/=p;
    }
    return res;
}

vector<int> mul(vector<int> a,int b)
{
    vector<int> c;
    int t=0;
    for(int i=0;i<a.size();++i)
    {
        t+=a[i]*b;
        c.push_back(t%10);
        t/=10;
    }
    while(t)
    {
        c.push_back(t%10);
        t/=10;
    }
    return c;
 }

: Cn=1n+1Cn2n: C1=1Cn=Cn14n2n+1Cn+1=C0Cn+C1cn1++CnC0: Cn=1n+1Cn2n: C1=1Cn=Cn14n2n+1Cn+1=C0Cn+C1cn1++CnC0(129)(130)(131)

/*
给定 n 个 0 和 n 个 1,它们将按照某种顺序排成长度为 2n 的序列,求它们能排列成的所有序列中,能够满足任意前缀序列中 0 的个数都不少于 1 的个数的序列有多少个。
输出的答案对 109+7 取模。
输入格式
共一行,包含整数 n。
输出格式
共一行,包含一个整数,表示答案。
数据范围
1≤n≤105
输入样例:
3
输出样例:
5
/*
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int mod=1e9+7;
int qmi(int a,int k,int p);

int main()
{
    int n;
    cin>>n;
    int a=2*n,b=n;
    LL res=1;
    for(int i=a;i>=a-b+1;--i)
    {
        res=(LL)res*i%mod;
    }
    for(int i=b;i>0;--i)
    {
        res=(LL)res*qmi(i,mod-2,mod)%mod;
    }
    res=(LL)res*qmi(n+1,mod-2,mod)%mod;
    cout<<res<<endl;
    return 0;
}

int qmi(int a,int k,int p)
{
    LL res=1;
    while(k)
    {
        if(k&1)
        {
            res=(LL)res*a%p;
        }
        a=(LL)a*a%p;
        k>>=1;
    }
    return res;
}

容斥原理

AB.ABC.AB.ABC.(132)(133)

广n:|S1S2S3Sn|=0in(Si) 0i<jn(SiSj) +0i<j<kn(SiSjSk)+(1)n1(SiSjSn)(1+x)nx=1,C0n+C1n+C2n++Cnn=2nxk(1kn)1.=C1kC2k+C3k+(1)n1Ckk(1+x)nx=1=1.(:)广n:|S1S2S3Sn|=0in(Si) 0i<jn(SiSj) +0i<j<kn(SiSjSk)+(1)n1(SiSjSn)(1+x)nx=1,C0n+C1n+C2n++Cnn=2nxk(1kn)1.=C1kC2k+C3k+(1)n1Ckk(1+x)nx=1=1.(:)(134)(135)(136)(137)(138)(139)

1n1m1npnpnp1p2mnp1p2pm.().+.使kkm12m12m1.1n1m1npnpnp1p2mnp1p2pm.().+.使kkm12m12m1.(140)(141)(142)(143)(144)

/*
给定一个整数 n 和 m 个不同的质数 p1,p2,…,pm。
请你求出 1∼n 中能被 p1,p2,…,pm 中的至少一个数整除的整数有多少个。
输入格式h
第一行包含整数 n 和 m。
第二行包含 m 个质数。
输出格式
输出一个整数,表示满足条件的整数的个数。
数据范围
1≤m≤16,
1≤n,pi≤109
输入样例:
10 2
2 3
输出样例:
7
*/
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N=20;
int p[N],n,m;

int main()
{
    cin>>n>>m;
    for(int i=0;i<m;++i)
    {
        cin>>p[i];
    }
    int res=0;
    for(int i=1;i<1<<m;++i)
    {
        int t=1,cnt=0;
        for(int j=0;j<m;++j)
        {
            if(i>>j&1)
            {
                ++cnt;
                if((LL)t*p[j]>n)
                {
                    t=-1;
                    break;
                }
                t*=p[j];
            }
        }
        if(t==-1)
            continue;
        if(cnt%2)
            res+=n/t;
        else
            res-=n/t;
    }
    cout<<res<<endl;
    return 0;
}

博弈论

(ICG):.....NIM.(ICG):.....NIM.(145)(146)(147)(148)(149)(150)(151)

NIM().: .(ain1in) a1a2an=0.() a1a2an !=0.:a1a2an=x;x1kaik1axi<aiaiai(aix)aiaix0.:000=0.a1a2an=0aiaia1a2aian=0ai=ai..NIM().: .(ain1in) a1a2an=0.() a1a2an !=0.:a1a2an=x;x1kaik1axi<aiaiai(aix)aiaix0.:000=0.a1a2an=0aiaia1a2aian=0ai=ai..(152)(153)(154)(155)(156)(157)(158)(159)(160)(161)(162)(163)

/*
给定 n 堆石子,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。
输入格式
第一行包含整数 n。
第二行包含 n 个数字,其中第 i 个数字表示第 i 堆石子的数量。
输出格式
如果先手方必胜,则输出 Yes。
否则,输出 No。
数据范围
1≤n≤105,
1≤每堆石子数≤109
输入样例:
2
2 3
输出样例:
Yes
*/
#include <iostream>
using namespace std;

int main()
{
    int n;
    scanf("%d",&n);
    int res=0;
    
    while(n--)
    {
        int x;
        scanf("%d",&x);
        res^=x;
    }
    if(res)
        puts("Yes");
    else
        puts("No");
    return 0;
}

西MexSG.MexkSG(y)=Mex(SG(y1),SG(y2),SG(y3)SG(yk)).SG(x)=0SG(x)!=0.,SG(1)SG(2)SG(n)=0.SG(1)SG(2)SG(n)!=0.NIM.使(DP).西MexSG.MexkSG(y)=Mex(SG(y1),SG(y2),SG(y3)SG(yk)).SG(x)=0SG(x)!=0.,SG(1)SG(2)SG(n)=0.SG(1)SG(2)SG(n)!=0.NIM.使(DP).(164)(165)(166)(167)(168)(169)(170)(171)

/*
给定 n 堆石子以及一个由 k 个不同正整数构成的数字集合 S。
现在有两位玩家轮流操作,每次操作可以从任意一堆石子中拿取石子,每次拿取的石子数量必须包含于集合 S,最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。
输入格式
第一行包含整数 k,表示数字集合 S 中数字的个数。
第二行包含 k 个整数,其中第 i 个整数表示数字集合 S 中的第 i 个数 si。
第三行包含整数 n。
第四行包含 n 个整数,其中第 i 个整数表示第 i 堆石子的数量 hi。
输出格式
如果先手方必胜,则输出 Yes。
否则,输出 No。
数据范围
1≤n,k≤100,
1≤si,hi≤10000
输入样例:
2
2 5
3
2 4 7
输出样例:
Yes
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <unordered_set>
using namespace std;
const int N=110,M=10010;
int s[N],f[M];
int k,n;
int sg(int x);

int main()
{
    scanf("%d",&k);
    for(int i=0;i<k;++i)
    {
        scanf("%d",&s[i]);
    }
    
    memset(f,-1,sizeof f);
    
    scanf("%d",&n);
    int res=0;
    for(int i=0;i<n;++i)
    {
        int x;
        scanf("%d",&x);
        res^=sg(x);
    }
    if(res)
        puts("Yes");
    else
        puts("No");
    return 0;
}

int sg(int x)
{
    if(f[x]!=-1)
        return f[x];
    unordered_set<int> S;
    for(int i=0;i<k;++i)
    {
        int sum=s[i];
        if(x>=sum)
            S.insert(sg(x-sum));
    }
    for(int i=0;;++i)
    {
        if(!S.count(i))
        return f[x]=i;
    }
}
posted @   “账号已注销”  阅读(113)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示