文章目录
- 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;
}