数学专题
- 一、进制转换类
- 1. 模板
- 2. 例题
- 二、公式推导类
- 1. 一元二次方程解
- 2. 例题
- 2.1 【模板】怪物同笼
- 2.2 K K K 的倍数
- 三、枚举
- 例题
- 1. 二次方程
- 2. 【模板】立方体体积
- 3. 街头篮球
一、进制转换类
1. 模板
1.1 十转 x x x
while(n){num[++idx]=num2digit(x%i);x/=i;
}
1.2 x x x 转十
for(int i=s.size()-1;i>=0;i--){if(isdigt(s[i]))num=(num*10)+(s[i]-'0');else num=(num*10)+(10+s[i]-'A');
}
2. 例题
给定一个数 n n n( 1 ≤ n ≤ 1 0 18 1\le n\le10^{18} 1≤n≤1018),求 2 ∼ 16 2\sim16 2∼16 进制下该数是否为一个上升数(每位都满足 a i ≤ a i − 1 a_i\le a_{i-1} ai≤ai−1)。输出若干行,每行两个空格隔开的数,第一个数是对应进制,第二个数是对应进制下的上升数。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
signed main(){cin>>n;for(int i=2;i<=16;i++){//遍历每个进制int num[55]={},idx=0,flag=0,x=n;//计算i进制下的n,存储到num[]中while(x){num[++idx]=x%i;x/=i;}//判断是否为上升数for(int i=idx-1;i>=1;i--)if(num[i]<num[i+1]){flag=1;break;}if(flag)continue;//按要求输出cout<<i<<" ";for(int i=idx;i>=1;i--)if(num[i]<10)cout<<num[i];else cout<<(char)('A'+(num[i]-10));cout<<endl;}return 0;
}
二、公式推导类
1. 一元二次方程解
已知:
a x 2 + b x + c = 0 ax^2+bx+c=0 ax2+bx+c=0
则:
x = − b ± b 2 − 4 a c 2 a x = \frac{-b ± \sqrt{b^2 - 4ac}}{2a} x=2a−b±b2−4ac
推导过程:
等式两边都 ÷ a \div a ÷a,得:
x 2 + b a x + c a = 0 x^2+\frac{b}{a}x+\frac{c}{a}=0 x2+abx+ac=0
移项,得:
x 2 + b a x = - c a x^2+\frac{b}{a}x=-\frac{c}{a} x2+abx=-ac
方程两边都加上一次项系数 b a \frac{b}{a} ab 的一半的平方,即方程两边都加上 b 2 4 a 2 \frac{b^2}{4a^2} 4a2b2,(配方)得
x 2 + b a x + b 2 4 a 2 = b 2 4 a 2 - c a x^2+\frac{b}{a}x+\frac{b^2}{4a^2}=\frac{b^2}{4a^2}-\frac{c}{a} x2+abx+4a2b2=4a2b2-ac
( x + b 2 a ) 2 = b 2 − 4 a c 4 a 2 x + b 2 a = ± b 2 − 4 a c 2 a x = − b ± b 2 − 4 a c 2 a (x+\frac{b}{2a})^2=\frac{b^2-4ac}{4a^2} \\ x+\frac{b}{2a}=±\frac{ \sqrt{b^2-4ac} }{2a} \\ x=\frac{-b±\sqrt{b^2-4ac}}{2a} (x+2ab)2=4a2b2−4acx+2ab=±2ab2−4acx=2a−b±b2−4ac
2. 例题
2.1 【模板】怪物同笼
有 n n n 组数据,给出第 1 1 1 种怪物有 a a a 个头 b b b 条腿,第 2 2 2 种怪物有 c c c 个头 d d d 条腿。所有怪物加起来有 e e e 个头 f f f 条腿。
思路:我们可以得出如下方程组( x x x 为怪物 1 1 1 的数量, y y y 为怪物 2 2 2 的数量):
{ a x + c y = e b x + d y = f \begin{cases} ax+cy=e \\ bx+dy=f \\ \end{cases} {ax+cy=ebx+dy=f
因此我们就推导得出 x x x 的求解公式:
a d x + c d y − b c x + c d y ( a d − b c ) x = d e − c f x = d e − c f a d − b c adx+cdy-bcx+cdy \\ (ad-bc)x=de-cf \\ x=\frac{de-cf}{ad-bc} adx+cdy−bcx+cdy(ad−bc)x=de−cfx=ad−bcde−cf
以及 y y y 的求解公式:
a b x + a d y − a b x + b c y ( a d − b c ) y = a f − b e y = a f − b e a d − b c abx+ady-abx+bcy \\ (ad-bc)y=af-be\\ y=\frac{af-be}{ad-bc} abx+ady−abx+bcy(ad−bc)y=af−bey=ad−bcaf−be
AC \text{AC} AC 代码如下:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,a,b,c,d,e,f;
signed main(){cin>>t;while(t--){cin>>a>>b>>c>>d>>e>>f;int g=d*e-c*f;int h=a*d-b*c;int i=a*f-b*e;int j=a*d-b*c;if(g%h!=0||i%j!=0||g/h<0||i/j<0){printf("No Solution\n");continue;}printf("%d %d\n",(d*e-c*f)/(a*d-b*c),(a*f-b*e)/(a*d-b*c));}return 0;
}
2.2 K K K 的倍数
给定一个长度为 n n n 的序列 a a a,求 a i − a j m o d k = 0 a_i-a_j \mod k=0 ai−ajmodk=0 的个数( 1 ≤ i < j ≤ n 1\le i<j \le n 1≤i<j≤n)。
暴力枚举( “ “ “差缀和 ” ” ” ):
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k,cnt;
int a[200010];
int c[200010];
int s[200010];
signed main(){cin>>n>>k;for(int i=1;i<=n;i++){cin>>a[i];if(i>=2){c[i]=a[i]-a[i-1];s[i]=s[i-1]+c[i];}}for(int l=2;l<=n;l++)for(int r=l;r<=n;r++)if((s[r]-s[l-1])%k==0)cnt++;cout<<cnt;return 0;
}
优化(同余思想):
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXK=1e5+8;
int n,k,ans;
int cnt[MAXK];//存储j之前mod k=0,1,2,...,k-1的数分别有几个
signed main(){cin>>n>>k;for(int j=1,aj;j<=n;j++){cin>>aj;ans+=cnt[aj%k]++;}cout<<ans;return 0;
}
三、枚举
例题
1. 二次方程
记 s ( n ) s(n) s(n) 为正整数 n n n 的数码和,给出 a , b , c a,b,c a,b,c,求出所有满足 n = a × [ s ( n ) ] 2 + b × s ( n ) + c n=a\times[s(n)]^2+b\times s(n)+c n=a×[s(n)]2+b×s(n)+c的正整数 n n n。
经典 G \text{G} G 法:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a,b,c;
int s(int n){int ret=0;while(n){ret+=n%10;n/=10;}return ret;
}
signed main(){cin>>a>>b>>c;for(int i=1;i<=1e7;i++)if(a*s(i)*s(i)+b*s(i)+c==i)cout<<i<<" ";return 0;
}
优化方法:
思想:反向枚举
n n n 的范围很大(不超过 9223372036854775807 9223372036854775807 9223372036854775807,我就粗略地说 1 0 20 10^{20} 1020 了)。但是 s ( n ) s(n) s(n) 最大也就 9 × 20 = 180 9\times 20=180 9×20=180。因此我们可以枚举 s ( n ) s(n) s(n),从 1 ∼ 180 1 \sim 180 1∼180,通过 n = a × [ s ( n ) ] 2 + b × s ( n ) + c n=a\times[s(n)]^2+b\times s(n)+c n=a×[s(n)]2+b×s(n)+c 算出 n n n,再通过 s n = S ( n ) sn = S(n) sn=S(n) 进行验算。 AC \text{AC} AC 代码如下:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a,b,c;
int s(int n){int ret=0;while(n){ret+=n%10;n/=10;}return ret;
}
signed main(){cin>>a>>b>>c;for(int sn=1;sn<=180;sn++){int n=a*sn*sn+b*sn+c;if(sn==s(n))cout<<n<<" ";}return 0;
}
2. 【模板】立方体体积
有一个立方体,它的长、宽、高是 a , b , c a,b,c a,b,c,立方体的体积等于长、宽、高的乘积。现在只知道立方体的体积等于 n n n,问立方体有多少种不同的可能的形状?长、宽、高顺序不同视作不同形状。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,ans;
signed main(){cin>>n;for(int a=1;a*a*a<=n;a++){//不妨令a<=b<=cif(n%a!=0)continue;for(int b=a;b*b<=n/a;b++){int c=n/a/b;if(a*b*c==n){if(a==b&&b==c)ans++;else if(a==b||b==c)ans+=3;else ans+=6;}}}cout<<ans;return 0;
}
3. 街头篮球
教练想要从他带领的 n n n 名选手中选出一支篮球队。
每名选手的能力为整数,第 i i i 名选手的能力为 a i a_i ai。篮球队的队员数量必须是 3 3 3 人,一支队伍的总能力就是所有队员能力的总和。教练比较迷信,他的幸运数字是 m m m,所以他要求队伍的总能力必须是 m m m 的倍数。请帮他算一下,符合这个要求的队伍组合有多少?
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,ans;
int cnt[1005];
int cal(int x,int y,int z){if(x==y&&y==z)return 1ll*cnt[x]*(cnt[x]-1)*(cnt[x]-2)/6;if(x==y)return 1ll*cnt[x]*(cnt[x]-1)/2*cnt[z];if(y==z)return 1ll*cnt[x]*cnt[y]*(cnt[y]-1)/2;return 1ll*cnt[x]*cnt[y]*cnt[z];
}
signed main(){cin>>n>>m;for(int i=1,a;i<=n;i++){cin>>a;cnt[a%m]++;}for(int i=0;i<m;i++)for(int j=i;j<m;j++){int k=(2*m-i-j)%m;if(j<=k)ans+=cal(i,j,k);}cout<<ans;return 0;
}