文章目录

  • HENAU 冬令营 数学专题
  • 知识汇总
  • 题目列表
  • 快输模板
  • A - A^B Mod C
  • B - 逆元
  • C - 判决素数个数
  • 方法一:埃氏筛法
  • 方法二:区间筛法
  • D - 矩阵乘法
  • E - Bash游戏
  • F - 取石子游戏
  • G - Matches Game
  • H - 互质数的个数(一)
  • I - Sumdiv
  • 知识点
  • J - The Lottery
  • 知识点
  • K - 组合数问题
  • 知识点
  • L - 同余方程
  • 知识点


题目列表

快输模板

import java.io.*;
import java.util.*;
public class Main {
    static class FastReader{
        BufferedReader br;
        StringTokenizer st;

        public FastReader(){br=new BufferedReader(new InputStreamReader(System.in));}

        String next(){
            while (st==null||!st.hasMoreElements()) {
                try {st = new StringTokenizer(br.readLine());}
                catch (IOException e) {e.printStackTrace();}}
            return st.nextToken();}

        int nextInt(){return Integer.parseInt(next());}

        long nextLong(){return Long.parseLong(next());}

        boolean hasNext(){
            if (st!=null && st.hasMoreTokens())
                return true;
            try {
                st=new StringTokenizer(br.readLine());
            } catch (Exception e) {
                return false;
            }
            return true;
        }
    }

    static PrintWriter out=new PrintWriter(
            new BufferedWriter(new OutputStreamWriter(System.out)));
    /*
    核心代码区
	*/
}

A - A^B Mod C

public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int a=sc.nextInt();
        int b=sc.nextInt();
        int c=sc.nextInt();
        System.out.println(pow(a,b,c));
    }
    public static long pow(long a,long b,long p){
    	//快速幂
        long res=1;
        while(b!=0){
            if((b&1)==1)res=res*a%p;
            a=a*a%p;
            b=b>>1;
        }
        return res;
    }

B - 逆元

public static void main(String[] args) {
        FastReader sc=new FastReader();
        //P<=2^31,如果令p为int类型数据,会RE
        long p=sc.nextLong();
        //一个数有逆元的充分必要条件是该数与模互质
        if(!isprime(p)) System.out.println("AKCniubi");
        else {
            if(p==2)
                System.out.println(1);
            else
            //1~p-1的逆元即是范围[1,p-1]
                System.out.println((p-1)/2*p);
        }
    }
    public static boolean isprime(long x){
        for(long i=2;i*i<=x;i++)
            if(x%i==0)
                return false;
        return true;
    }

C - 判决素数个数

static boolean isprime[]=new boolean[100005];//判断是否为素数
    static int prime[]=new int[100000];//存素数
    public static void main(String[] args) {
        FastReader sc=new FastReader();
        int x=sc.nextInt();
        int y=sc.nextInt();
        int cnt=0;//素数个数
        if(x>y){//保证x是左区间,y是右区间
            int t=x;
            x=y;
            y=t;
        }
        isprime[1]=true;//1不是素数
        for(int i=2;i<=y;i++){
            if(!isprime[i]){//如果i是素数
                prime[cnt++]=i;
                for(int j=i*2;j<=y;j+=i){
                    isprime[j]=true;
                }
            }
        }
        int sum=0;//给定区间内素数个数
        for(int i=0;i<cnt;i++){
            if(prime[i]>=x)sum++;
        }
        out.println(sum);out.flush();
    }

方法二:区间筛法

/*
    b以内的合数的最小质因数一定不超过sqrt(b)。
    如果有sqrt(b)以内的素数表的话,就可以把埃式筛法运用在[a,b)上了。
    也就是说,先分别做好[2,sqrt(b))的表和[a,b)的表,
    然后从[2,sqrt(b))的表中筛得素数的同时,
    也将其倍数从[a,b)的表中划去,最后剩下的就是区间[a,b)内的素数了
     */
    static boolean is_prime[]=new boolean[100005];
    static boolean is_prime_small[]=new boolean[100005];
    public static void main(String[] args) {
        FastReader sc=new FastReader();
        int x=sc.nextInt();
        int y=sc.nextInt();
        if(x>y){//保证x是左区间,y是右区间
            int t=x;x=y;y=t;
        }
        int sum=0;
        segment_sieve(x,y+1);
        for(int i=0;i<y+1-x;i++){
            if(is_prime[i])sum++;
        }
        if(x==1)sum--;
        out.println(sum);out.flush();
    }
    static void segment_sieve(int a,int b)
    {//获得区间[a,b)内素数个数
        for(int i=0;i*i<b;++i) is_prime_small[i]=true; //初始化
        for(int i=0;i<b-a;++i) is_prime[i]=true; //初始化,注意下标变化,为了省空间
        for(int i=2;i*i<b;++i) {
            if(is_prime_small[i]) {
                for(int j=2*i;j*j<b;j+=i) is_prime_small[j]=false;  //筛选[2,sqrt(b));
                //(a+i-1)/i得到最接近a的i的倍数,最低是i的2倍,然后筛选
                for(int j=Math.max(2,(a+i-1)/i)*i;j<b;j+=i) is_prime[j-a]=false;
            }
        }
    }

D - 矩阵乘法

public static void main(String[] args) {
        FastReader sc=new FastReader();
        int n=sc.nextInt();
        int m=sc.nextInt();
        int k=sc.nextInt();
        long a[][]=new long[n][m];//矩阵1
        long b[][]=new long[m][k];//矩阵2
        long sum=0;//结果矩阵的元素
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++)
                a[i][j]=sc.nextInt();
        }
        for(int i=0;i<m;i++){
            for(int j=0;j<k;j++)
                b[i][j]=sc.nextInt();
        }
        for(int i=0;i<n;i++){
            for(int j=0;j<k;j++){
                sum=0;
                for(int o=0;o<m;o++)
                    sum+=a[i][o]*b[o][j];
                out.print(sum+" ");
            }
            out.println();
        }
        out.flush();
    }

E - Bash游戏

/*
    每次最多可以取k个,当轮到己方时还有k+1个则必输
    因此总数是(k+1)的倍数则必输,否则必赢
     */
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        long t=sc.nextLong();//组数
        long n,k;//石子总数,最多可以拿的石子数量
        while(t-->0){
            n=sc.nextLong();
            k=sc.nextLong();
            k++;
            if(n%k!=0)System.out.println("A");
            else System.out.println("B");
        }
    }

F - 取石子游戏

//威佐夫博弈
	public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int a,b;//两堆石子数量
        while(sc.hasNext()){
            a=sc.nextInt();
            b=sc.nextInt();
            //己方胜利输出1,否则输出0
            if(a>b){
                int tmp=a;a=b;b=tmp;
            }
            //奇异局势,先手输
            if(a==(int)((b-a)*(1+Math.sqrt(5.0))/2))
                System.out.println(0);
            else System.out.println(1);
        }
    }

G - Matches Game

使用快输,在vjudge上会RE;POJ上能AC
//尼姆博弈
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int t;//堆数
        while(sc.hasNext()){
            t=sc.nextInt();
            int s=sc.nextInt();
            for(int i=1;i<t;i++){
                s^=sc.nextInt();
            }
            //奇异局势,先手输
            if(s==0)System.out.println("No");
            else System.out.println("Yes");
        }
    }

H - 互质数的个数(一)

//欧拉函数求互质的个数
    public static void main(String[] args) {
        FastReader sc=new FastReader();
        int t=sc.nextInt();//组数
        long n;
        while(t-->0){
            n=sc.nextLong();
            out.println(euler_deal(n));
            out.flush();
        }
    }
    public static long euler_deal(long n){
        long res=n;
        for(long i=2;i*i<=n;i++){
            if(n%i==0){
                res=res/i*(i-1);
                for(;n%i==0;n/=i);
            }
        }
        //n本身为质数
        if(n!=1) res=res/n*(n-1);
        return res;
    }

I - Sumdiv

知识点

1)等比数列求和推导及优化 2)分治法求等比数列和 3)思路来源1:C++版 4)思路来源2:Java版

static int mod=9901;
    static int maxn=10010;
    static int tot;
    static long p[],q[];//记录质因子及质因子出现次数
    public static void main(String[] args) {
        FastReader sc=new FastReader();
        long n=sc.nextLong();
        long m=sc.nextLong();
        long ans=1;
        //特判
        if(n==0) out.println(0);
        else if(n==1||m==0) out.println(1);
        else {
            p = new long[maxn];
            q = new long[maxn];
            factor(n);
            for (int i=1;i<=tot;i++) q[i]*=m;
            for (int i=1;i<=tot;++i) {
                ans *= getsum(p[i],q[i]);
                ans %= mod;
            }
            out.println(ans);
        }
        out.flush();
    }
    //快速幂
    public static long pow(long a,long b){
        long ans=1;
        while(b!=0) {
            if((b&1)==1) ans=ans*a%mod;
            a=a*a%mod;
            b>>=1;
        }
        return ans;
    }
    //等比数列求和
    public static long getsum(long a, long b) {
        if(b==0) return 1;
        if((b&1)==1) return getsum(a,b/2)*(1+pow(a,b/2+1))%mod;
        else return (getsum(a,b/2-1)*(1+pow(a,b/2+1))+pow(a,b/2))%mod;
    }
    //统计质因子出现次数
    static void factor(long n) {
        tot=0;
        long now=n;
        for (int i=2;i*i<=n;++i) {
            if (now%i==0) {
                p[++tot] = i;
                q[tot] = 0;
                while (now % i == 0) {
                    ++q[tot];
                    now /= i;
                }
            }
        }
        if (now!=1) {//特判:n本身为质数
            p[++tot] = now;
            q[tot] = 1;
        }
    }

J - The Lottery

知识点

/*
    求1~n中非数组a的数的倍数的数,就是把1~n中数组a的数的倍数筛掉,剩下的数的个数就是结果。
    暴力跑会超时,利用容斥原理,比如n=10,m=2,a[0]=2,a[1]=3,把1到20中所有2的倍数筛掉,
    先令ans=n=20,ans=ans-n/2=10。再把1到20中所有3的倍数筛掉,ans=ans-n/3=4。
    那么现在问题来了,这么筛选的话会导致2和3的公倍数进行了二次筛选,也就是6,12,18这三个数,
    所以要把这三个数加回来,ans=ans+n/(2*3)=7,得出最终结果。
    以此类推,数的个数为奇数就减,数的个数为偶数就加,这就是容斥原理。
     */
    static int a[],m;//数组a存储m个数
    static long n,ans;//ans存储最终结果
    public static void main(String[] args) {
        FastReader sc=new FastReader();
        while(sc.hasNext()){
            n=sc.nextLong();//范围1~n
            m=sc.nextInt();//给定m个数
            a=new int[m];
            for(int i=0;i<m;i++)a[i]=sc.nextInt();
            ans=n;//一开始结果为n,逐步减去m个数的倍数
            for(int i=0;i<m;i++) {
                ans=ans-n/a[i];
                dfs(a[i],i+1,2);
            }
            out.println(ans);out.flush();
        }
    }
    //最大公因数
    public static long gcd(long a,long b) {return (b!=0)?gcd(b,a%b):a;}
    //最小公倍数
    public static long lcm(long a,long b) {return a/gcd(a,b)*b;}
    public static void dfs(long hav,int cur,long num)
    {//hav为当前数,cur为当前数的下标
        if(hav>n||cur>=m) return;
        for(int i=cur;i<m;i++)
        {
            long temp=lcm(hav,a[i]);
            if((num&1)==1)//数的个数为奇数,则筛掉
                ans=ans-n/temp;
            else//数的个数为偶数,则加回来
                ans=ans+n/temp;
            dfs(temp,i+1,num+1);
        }
    }

K - 组合数问题

知识点

static int t,k,n,m;
    static int c[][]=new int[2005][2005];//存储c(i,j)
    //对于所有的0≤i≤n,0≤j≤min(i,m),满足c(i,j)是k的倍数的总数
    //类似于求矩阵和
    static int s[][]=new int[2005][2005];
    public static void main(String[] args) {
        FastReader sc=new FastReader();
        for(int i=0;i<2005;i++){
            Arrays.fill(c[i],0);
            Arrays.fill(s[i],0 );
        }
        t=sc.nextInt();
        k=sc.nextInt();
        prepare();//预处理
        while(t-->0){
            n=sc.nextInt();
            m=sc.nextInt();
            if(m>n) m=n;
            out.println(s[n][m]);out.flush();
        }
    }
    //预处理:前缀和+递推打表
    public static void prepare(){
        c[1][1]=1;
        for(int i=0;i<=2000;i++) c[i][0]=1;
        for(int i=2;i<=2000;i++){
            for(int j=1;j<=i;j++){
                c[i][j]=(c[i-1][j]+c[i-1][j-1])%k;
            }
        }
        for(int i=2;i<=2000;i++){
            for(int j=1;j<=i;j++){
                //求前缀和,口诀:上加左 减左上 加自己
                s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
                if(c[i][j]==0) s[i][j]+=1;
            }
            s[i][i+1]=s[i][i];
        }
    }

L - 同余方程

static long x,y;//目前方程真正的解
    public static void main(String[] args) {
        FastReader sc = new FastReader();
        long a=sc.nextLong();
        long b=sc.nextLong();
        exgcd(a,b);
        //我们求出来的x必然满足方程,但不一定是最小正整数解,所以要进行答案处理
        //括号中取模再加,可以处理负数的情况
        x=(x%b+b)%b;
        out.println(x);out.flush();
    }
    //扩展欧几里得
    public static void exgcd(long a,long b) {
        //当前目的:求解 ax + by = gcd(a, b) 这么一个方程
        if(b==0) {//a, b不断改变的过程中,b最终必然会成为0
            //在b=0时方程还要成立? 使x=1,y=0, 必然成立
            x = 1;
            y = 7; //建议返回0。不过y=7能AC,证明了最后一个等式不受最后一个y影响
            return;
        }
        exgcd(b,a % b);//把下一层系数传进去(先求下一个方程的解 )
        //现在我们已经拿到了下一个方程的解x, y
        long nx=x;//暂存一下x
        x=y;
        y = nx-a/b*y;
    }