一些问题利用已知数学知识或公式,可使问题简化
原理:gcd(a,b) = gcd(b,a%b)
推广 a,b 的公约数与 a%b, b 的全部相等
int gcd(int a, int b){
return b?gcd(b, a%b):a;
}
l c m ( a , b ) = a ∗ b / g c d ( a , b ) lcm(a,b) = a*b/gcd(a, b) lcm(a,b)=a∗b/gcd(a,b)
struct Fracture{
int up, down;
}
假分数的三项规则
定理一:任意一个大于1的自然数可以拆分成质因子乘积
n = 2 e 1 ∗ 3 e 2 ∗ 5 e 3 ∗ 7 e 4 ∗ 1 1 e 5 + . . . . . . ( n > 1 ) n = 2^{e1}*3^{e2}*5^{e3}*7^{e4}*11^{e5}+......\ \ \ \ \ \ \ \ \ \ \ \ ( n>1) n=2e1∗3e2∗5e3∗7e4∗11e5+...... (n>1)
且 n 函数决定 序列 {e1, e2, e3, e4, e5 …}
推论1:n的因子个数为(n>1):
∏ i = 1 , 2 , 3 , . . . ( 1 + e i ) \prod_{i=1,2,3,...}{(1+e_i)} i=1,2,3,...∏(1+ei)
推论2:n 的因子之和
∏ i = 1 , 2 , 3 , . . . ( 1 + p i 1 + . . . + p i e i ) = 1 − p 1 e 1 + 1 1 − p 1 ∗ 1 − p 2 e 2 + 1 1 − p 2 ∗ . . . ∗ 1 − p k e k + 1 1 − p k \prod_{i=1,2,3,...}{(1+p_i^1+...+p_i^{e_i})}=\frac{1-p_1^{e_1+1}}{1-p_1}*\frac{1-p_2^{e_2+1}}{1-p_2}*...*\frac{1-p_k^{e_k+1}}{1-p_k} i=1,2,3,...∏(1+pi1+...+piei)=1−p11−p1e1+1∗1−p21−p2e2+1∗...∗1−pk1−pkek+1
推论3:对于自然数 n 只存在 1 个或 0个质数 大于 n \sqrt{n} n
反证: 若存在因子 pi, pj > n \sqrt{n} n, 则 pi,*pj也是因子,而 p i , ∗ p j > n p~i~,*p~j~>n p i ,∗p j >n
拓展一:大浮点数
- 麻烦的地方在于小数部分的对齐,同时低位为0时不输出
拓展二:进制转换
- 原理和一般的进制转换一样,只不过需要使用一些大整数作为中间变量
- 任何进制互转都可以采用逐次除余的方法。
数据结构:
class Bign{
private:
int d[1000];
int len;
public:
Bign(){
memset(d,0,sizeof(d));
len=0;
}
void change(char str[]);
int compare(const Bign&b) const;
void show();
Bign add(const Bign &b) const;
Bign sub(const Bign &b) const;
Bign prod(int multiplier) const;
Bign div(int divisor, int &remainder);
void vert(Bign &c, int basef, int baseto); // 进制转换
};
四则运算
Bign Bign::sub(const Bign &b1)const{
if(compare(b1)<0){ // 如果b1 更大,则调用 b1的sub
return b1.sub(*this);
}
Bign c;
int carry=0;
for(int i=0;i=b1.d[i]){
c.d[i]=d[i]-b1.d[i]-carry;
carry=0;
}else if(d[i]-carry1&&c.d[c.len-1]==0){ // 去掉多余的0,且保证至少有一位
c.len--;
}
return c;
}
Bign Bign::prod(int multiplier) const{
int carry=0,tmp;
Bign c;
for(int i=0;i0){
c.d[c.len++] = carry%10;
carry/=10;
}
return c;
}
Bign Bign::div(int divisor, int &remainder){
Bign c;
int dividend=0, init=1;
for(int i=len-1;i>=0;i--){
dividend = dividend*10+d[i];
c.d[i]=dividend / divisor;
dividend %= divisor;
}
remainder = dividend; // 可以在整个函数中不使用,dividend,用remainder替换。
c.len = len;
while(c.len>1&&c.d[c.len-1]==0){
c.len--;
}
return c;
}
void Bign::vert(Bign &c, int basef, int baseto){
int r=0;
c.len=0;
Bign t=*this;
while(len!=0){
r=0;
// 除余
for(int i=len-1;i>=0;i--){
r=r*basef+d[i];
d[i]=r/baseto;
r%=baseto;
}
while(d[len-1]==0&&len>0)
len--;
// 余数赋值给结果
c.d[c.len++]=r;
}
*this=t;
}
int exGcd(int a, int b, int &x, int&y){
if(b==0){
x=1;y=0; // 递推出口为 a=1*gcd ,b=0
return a;
}
int tmp, gcd;
gcd = exGcd(b, a%b, x, y);
tmp = x;
x = y;
y = tmp-a/b*y;
return gcd;
}
在得到一组解之后,可以得到全部解(K 为任意整数):
x = x 0 + b g c d ∗ K y = y 0 + b g c d ∗ K x=x_0+\frac{b}{gcd}*K \\ y=y_0+\frac{b}{gcd}*K x=x0+gcdb∗Ky=y0+gcdb∗K
还可以得到最小的正x, 正y。(主要考虑x0,y0为负数的情况):
x m i n + = ( x % t + t ) % t , t = b g c d x^+_{min} = (x\%t+t)\%t, \ \ t=\frac{b}{gcd} xmin+=(x%t+t)%t, t=gcdb
推广一: a x + b y = c ax+by=c ax+by=c的求解
两边同时乘以 g c d c \frac{gcd}{c} cgcd,得
a x ′ + b y ′ = g c d x = x ′ × c g c d , y = y ′ × c g c d ax^{'}+by^{'}=gcd \\ x = x^{'} \times \frac{c}{gcd}, \ \ \ \ y = y^{'} \times \frac{c}{gcd} ax′+by′=gcdx=x′×gcdc, y=y′×gcdc
有解的条件为 c % g c d = = 0 c\%gcd==0 c%gcd==0, 因为x,y为整数,而可以证明 x ′ , y ′ 不 可 被 g c d 整 除 x^{'},y^{'}不可被gcd整除 x′,y′不可被gcd整除
推广二:同余式 a x = c ( m o d m ) ax=c(mod\ m) ax=c(mod m),求x
变形为 a x + b y = c ax+by=c ax+by=c
推广三:逆元的求解以及 (b/a)%m的计算
逆元:若 a b = 1 ( m o d m ) ab=1(mod\ m) ab=1(mod m), 则称a和b互为模m的逆元一般记作 a = 1 b ( m o d m ) a=\frac{1}{b}(mod\ m) a=b1(mod m),反之亦然
逆元作用:通过找到 a 的模 m 逆元 x ,有
( a / b ) % m = ( a ∗ x ) % m (a/b)\%m=(a*x)\%m (a/b)%m=(a∗x)%m
所以,求 a 的模 m 逆元, 相当于求 x , a x = 1 ( m o d m ) ax=1(mod\ m) ax=1(mod m)
补充知识:求 n! 有多少个质因子 p
结论: n p + n p 2 + n p 3 + … \frac{n}{p}+\frac{n}{p^2}+\frac{n}{p^3}+\dots pn+p2n+p3n+…
问题一:求 C n m \bf C_n^m Cnm
问题二:如何计算 C n m % p \ \bf C_n^m\%p Cnm%p
int C(int n, int m, int p){
if(m==0||n==m){
return 1;
return (C(n-1,m-1)+C(n-1,m)) % p;
}
sort(第一个元素地址,最后一个元素的下一个地址,[比较函数])
典型问题:
(1)排列
贪心是用来解决一类最优化问题,并希望由局部最优解来推导得到全局最优解,贪心算法适用的问题一定满足最优子结构性质,即一个问题的最优解可以由它的子问题的最优解有效地构造出来。
严谨地使用贪心解决最优问题需要对所选策略进行证明,常用反证或归纳。
// 核心是保持要查找元素一致在 [letf, right]区间内,一旦left==right 说明查找成果
while(left
可能需要考虑当x位于[x,y]区间之外的情况
拓展: 将数组换成一个单调函数,用来解决在区间中寻找合理解
例题: 用N个线段(长度不同)首尾相接维持圆形,求最大半径
思路:将半径视作一个变量 r, 每个线段Li ,对应一个角度
θ i = f ( r , L i ) \theta_i=f(r, L_i) θi=f(r,Li)
⟹ \Longrightarrow ⟹ θ = ∑ θ i = ∑ f ( r , L i ) = 360 \theta=\sum{}\theta_i=\sum{}f(r,L_i)=360 θ=∑θi=∑f(r,Li)=360
也就是说 θ \theta θ 是关于r的单调函数,所以可以先估计出 r m i n , r m a x r_{min},r_{max} rmin,rmax, 然后在此区间寻找
F ( r 0 ) − 360 = e p s F(r_0)-360=eps F(r0)−360=eps
原理:
a b = { a b / 2 b & 1 = = 0 a ( b − 1 ) / 2 b & 1 ! = 0 a^b= \left\{ \begin{array}{lr} a^{b/2} & \ \ \ \ \ \ b\&1==0\\ a^{(b-1)/2} & b\&1!=0\\ \end{array} \right. ab={ ab/2a(b−1)/2 b&1==0b&1!=0
由此,以 问题 a b % m a^b\%m ab%m 为例
得到该问题的递归解法和非递归解法
typedef long long LL;//求a^b告m,递归写法
LL binaryPow(LL a, LL b,LL m) {
if(b == 0) return1; / /如果b为0,那么a^0=1//b为奇数,转换为b-1
if(b&2)
return a * binaryPow(a,b-1,m) % m;
else { //b为偶数,转换为b/2
LL mul = binaryPow(a, b /2, m);
return mul * mul%m;
}
}
LL binaryPow(LL a, LL b,LL m) {
LL ans =1;
while(b>0){
if(b&1){
ans = ans*a%m;
}
a = a*a%m;
b>>1;
}
return ans;
void mergeSort(int a[], int n){ // 非递归写法
int mid;
int *tmp = new int[n];
for(int step = 2; step / 2 < n;step *= 2){
for(int i=0;i
void quickSort(int a[], int left, int right){
if(lefttmp){
right--;
}
a[left] = a[right];
while(left
int randSelect(int a[], int left, int right, int k){
if(left==right){
return a[left];
}
int p = randPartition(a, left, right);
if(p==k-1){
return a[p];
}else if(p>k-1){
return randSelect(a,left, p-1,k);
}else if(ptmp)
j--;
a[i] = a[j];
while(i
一次只测试一组数据
在每次循环的时候要恢复初始状态,重置变量和数组。
一次测试所有数据
while…EOF
#include
int main(void){
int a,b;
// 在黑框中手动触发EOF:{Ctrl+Z}+Enter
while(scanf("%d%d",&a,&b)!=EOF){
printf("%d\n",a+b);
}
return 0;
}
// 第二种
while(gets(str)!=NULL){
......
}
while…break
#include
int main(){
int a,b;
while(scanf("%d%d",&a,&b)!=EOF, a||b){
printf("%d\n",a+b);
}
}
while(T–)
#include
int main(){
int n,a,b;
scanf("%d",&n);
while(n--){
scanf("%d%d",&a,&b);
printf("%d\n",a+b);
}
return 0;
}
由于浮点数运算过程会积累误差,所以应该引入极小数进行误差修正
常用:
const double eps=1e-8;
读入整行
char str[100];
cin.getline(str, 100);
string str;
getline(cin, str);
method | |
---|---|
strlen | |
strmp | |
strpy | |
strcat(str1, str2) |
char str[10]="abc";
sscanf(str,"%d", n);
ssprintf(str,"%d", n);
对数组每个元素赋相同的值
memset(a, 0, sizeof(a))
memset(a, -1, sizeof(a))
memset 按字节赋值,每个字节赋相同值。如果对数组赋其它数字,需要使用 fill(
函数 | 说明 |
---|---|
fabs(double) | – |
floor(double) | 返回double |
ceil(double) | 返回double |
pow(double, double) | – |
sqrt(double) | – |
log(double) | – |
sin,cos,tan(double) | – |
asin, acos, atan(double) | – |
round(double) | 四舍五入,返回double |
类型 | 大致范围 |
---|---|
int | ±2*109 |
long long(64b) | ±9*1018 |
float (32b) | ± 2128, 6,7位精度 |
double (64b) | ± 21024, 15,16位精度 |
- long long 初始化的时候要加后缀 LL, longlong的输入输出格式都是%lld
例:long long bignum = 1234567890LL- float,double 的 printf格式都是 %f, 但 double的scanf格式是%lf
(1)栈区
比2MB略小,大致相当于:
int p[5 * 105] 或 char p[2 * 106]
(2)全局区(静态区)
比 4 GB 小, 大致相当于:
int p[4 * 108] 或 char p[1 * 109]
(3)堆区
大小受限于计算机有效虚拟内存
# 精确到毫秒,要利用系统提供的接口
#include
#include
#include
char* log_Time(void){
struct tm *ptm;
struct timeb stTimeb;
static char szTime[19];
ftime(&stTimeb);
ptm = localtime(&stTimeb.time);
sprintf(szTime, "%02d-%02d %02d:%02d:%02d.%03d", ptm->tm_mon+1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec, stTimeb.millitm);
szTime[18] = 0;
return szTime;
}
# 精确到秒
time_t now = time(0);
tm *ltm = localtime(&now);
printf( "日期:%4d%02d%02d%\n时间:%02d%02d%02d\n星期: %d", 1900 + ltm->tm_year, 1 + ltm->tm_mon, ltm->tm_mday,
ltm->tm_hour,ltm->tm_min,ltm->tm_sec,ltm->tm_wday);
// 加法测试
cout<>3);
for(int i=1;i>5);
for(int i=1;i
例:
for(int i=1000,multi = 9000;i<9999&&multi<1e4;i++,multi+=9){ // 用 multi+=9替代 multi = i*9
panduan(i, multi);
}
题目:设a、b、c均是0到9之间的数字,abc、bcc是两个三位数,且有:abc+bcc=532。求满足条件的所有a、b、c的值。
int main(){
int num[3] = {0,0,0};
int total, total1, total2;
for(int a=0;a<6;a++){
total2 = a*100;
for(int b=0;b<=9;b++){
total1 = total2+b*10+b*100;
for(int c = 0;c<=9;c++){
total = total1+c+c+c*10;
if(total==532)
printf("%d %d %d\n",a,b,c);
}
}
}
return 0;
}