C数学基础专题

数学是程序竞赛中常见的题目,并且很容易和其他题目一起出,作为基础,还是得尽快掌握。

高精度计算

洛谷P1601 A+B高精度

C数学基础专题_第1张图片

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int MAXN = 505;
char  a1[MAXN], a2[MAXN];
int ans[MAXN];
int len1, len2;
void add(char* a1, char* a2) {
     
	int len1 = strlen(a1);
	int len2 = strlen(a2);
	int jin = 0;
	int yu = 0;
	int i, j;
	//从短的那边加,一旦短的加完,那么第一个for停止
	for (i = len2 - 1,j=len1-1;i >= 0;i--,j--) {
     
		int num = a1[j] - '0' + (a2[i] - '0')+jin;
		yu = num % 10;
		jin = num / 10;
		ans[j + 1] = yu;
	}
	//从长的那边加,短的加完了,要算长的那边,比如例子
	//1111111111111111111111111
	//9999999999
	for (j;j >= 0;j--) {
     
		int num = a1[j] - '0' + jin;
		yu = num % 10;
		jin = num / 10;
		ans[j + 1] = yu;
	}
	//上面没算ans[0],所以我们这边特别算,下面也有对0的特判
	if (jin > 0)ans[0] = jin;
}
int main() {
     
	cin>> a1 >> a2;
	len1 = strlen(a1);
	len2 = strlen(a2);
	if (len1 > len2) {
     
		add(a1, a2);
	}
	else
		add(a2, a1);
	int maxn = max(len1, len2);
	if (ans[0] != 0)cout << ans[0];
	for (int i = 1;i <= maxn;i++) {
     
		cout << ans[i];
	}

}

洛谷P1603 A*B高精度

C数学基础专题_第2张图片

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int MAXN =5000;
char  a1[MAXN], a2[MAXN];
int a[MAXN], b[MAXN];
int ans[MAXN];
int main() {
     
	cin >> a1 >> a2;
	a[0] = strlen(a1), b[0] = strlen(a2);
	//倒序模拟
	for (int i = 1;i <= a[0];i++)a[i] = a1[a[0] - i] - '0';
	for (int i = 1;i <= b[0];i++)b[i] = a2[b[0] - i] - '0';
	for (int i = 1;i <= a[0];i++) {
     
		for (int j = 1;j <= b[0];j++) {
     
			ans[i + j - 1] += a[i] * b[j];
		}
	}
	int len = a[0] + b[0];
	for (int i = 1;i <= len;i++) {
     
		if (ans[i] >= 10)ans[i + 1] += ans[i] / 10, ans[i] %= 10;
	}
	//除去前缀0
	while (ans[len] == 0 && len > 1)len--;
	for (int i = len;i >= 1;i--)cout << ans[i];
	return 0;
}

数论

快速幂

hdu2817 A sequence of numbers

C数学基础专题_第3张图片

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int MAXN =5000;
const int mod = 200907;
int N;
//等差数列
long long getadd(long long n, long long a1,long long d) {
     
	return (a1 + (n - 1) * d)%mod;
}
//等比数列,快速幂
long long getmult(long long  n, long long a1,long long q) {
     
	long long res = a1;
	long long base = q;
	n--;//因为等比数列的是n-1次方
	while (n) {
     
		if (n & 1) {
      res *= base; res %= mod; }
		base *= base;
		base %= mod;
		n >>= 1;
	}
	return res%mod;
}
int main() {
     
	cin >> N;
	while (N--) {
     
		long long a, b, c,K;
		cin >> a >> b >> c >> K;
		if (a - b == b - c) {
     
			cout << getadd(K, a, b - a) << endl;
		}
		else {
     
			cout << getmult(K, a, b / a) << endl;
		}
		
	}
}

hdu1061 Rightmost Digit

C数学基础专题_第4张图片

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int MAXN =5000;
const int mod = 200907;
int N;
//快速幂模板,因为只取最后一位,只要取模10
int fastm(int a, int n) {
     
	a %= 10;
	int base = a;
	int res = 1;
	while (n) {
     
		if (n & 1) {
     
			res *= base;
			res %= 10;
		}
		base *= base;
		base %= 10;
		n >>= 1;
	}
	return res %= 10;
}
int main() {
     
	cin >> N;
	while (N--) {
     
		int n;
		cin >> n;
		cout << fastm(n, n) << endl;
	}
	return 0;
}

hdu3117 Fibonacci Numbers

discuss里大佬的博客,证明前4位怎么得到的。http://www.cnblogs.com/WArobot/p/6810504.html
为何可以转为矩阵乘法,因为斐波那契是递归数列,矩阵不像常数,只能表示一个数,矩阵可以记录很多数,比如斐波那契数列,矩阵的00元素记录当前值,01记录前一个值,10和11没用,只是为了方便乘法。然后灵魂就是后面那个递归矩阵,他乘一下就可以代表递归的数运算一下,然后用快速幂。

C数学基础专题_第5张图片

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
int n;
//定义二维矩阵
struct mat {
     
	int m[2][2];
	mat() {
     
		memset(m, 0, sizeof m);
	}
};
//定义矩阵乘法运算,这里是2维,把2改为n便是n维
mat mult(mat m1, mat m2) {
     
	mat res;
	for (int i = 0;i < 2;i++) {
     
		for (int j = 0;j < 2;j++) {
     
			for (int k = 0;k < 2;k++) {
     
				res.m[i][j] = (res.m[i][j] + m1.m[i][k] * m2.m[k][j])%10000;
			}
		}
	}
	return res;
}
//矩阵快速幂
mat fastm(mat a, int n) {
     
	mat res;
	res.m[0][0] = 1, res.m[1][1] = 1;
	while (n) {
     
		if (n & 1) {
     
			res = mult(res, a);
		}
		a = mult(a, a);
		n >>= 1;
	}
	return res;
}
int main() {
     
	while (cin >> n) {
     
	//n==0特判
		if (n == 0) {
     
			cout << 0 << endl;
		}
	//n<=39打表
		else if (n <= 39) {
     
			int a = 1, b = 1;
			for (int i = 1;i <= n-2;i++) {
     
				int tmp = a;
				a = a + b;
				b = tmp;
			}
			cout << a << endl;
		}
		//n>=40,运用矩阵快速幂求后4位,找到运算公式求前4位
		else {
     
			mat mm;
			mm.m[0][0] = 1, mm.m[0][1] = 1, mm.m[1][0] = 1;
			mat ans2 = fastm(mm, n - 1);
			double ans1 = log10(1.0 / sqrt(5.0)) + log10((1 + sqrt(5.0)) / 2) * n;
			ans1 =ans1- (int)ans1;
			int ans = (int)(pow(10.0, ans1) * 1000);
			cout << ans << "...";
			//前置补0,用printf,自己补零可能会出错。
			printf("%04d\n", ans2.m[0][0]);
		}
	}
}

GCD&LCM

gcd:欧几里得算法以及欧几里得算法扩展
欧几里得b站大佬视频:https://www.bilibili.com/video/BV1Ra4y1h723/?spm_id_from=333.788.videocard.0
欧几里得扩展b站大佬视频:https://www.bilibili.com/video/BV1bp4y1S7bW?from=search&seid=6067462148398361649
lcm:两数乘积除去gcd

hdu1019 Least Common Multiple

C数学基础专题_第6张图片

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
int n;
long long gcd(long long a, long long b) {
     
	return b == 0 ? a : gcd(b, a % b);
}
long long lcm(long long a, long long b) {
     
	return a/ gcd(a, b) * b ;
}
int main() {
     
	cin >> n;
	while (n--) {
     
		int num;
		cin >> num;
		long long f;
		num--;
		cin >> f;
		while (num--) {
     
			long long l;
			cin >> l;
			//gcd要求大数在前面,小数在后面
			if (f > l)
				f = lcm(f, l);
			else f = lcm(l, f);
		}
		cout << f << endl;
	}
}

poj1061 青蛙的约会

推荐2篇大佬博客
1.https://blog.csdn.net/swordholy/article/details/4423543
2.https://blog.csdn.net/u011815404/article/details/88423181?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.channel_param
先1后2,慢慢看,对欧几里得扩展会有更深的认识。
C数学基础专题_第7张图片

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
int n;
ll gcd(ll a, ll b) {
     
	return b == 0 ? a : gcd(b, a % b);
}
ll lcm(ll a, ll b) {
     
	return a/ gcd(a, b) * b ;
}
ll t0,k0;
//引用可以很好的同时求出t,k
void extend_gcd(ll a, ll L, ll& t, ll& k) {
     
	if (L == 0) {
     
		t = 1, k = 0;
		return;
	}
	extend_gcd(L, a % L, t, k);
	//这里是由欧几里得gcd推理得到,还算简单
	ll tmp = t;
	t = k;
	k = tmp - (a / L) * k;
}
int main() {
     
	ll x, y, m, n, L;
	while (cin >> x >> y >> m >> n >> L) {
     
		ll a = m - n;
		ll b = y - x;
		//扩展gcd处理不了负数,所以取反,a和b是对应的
		if (a < 0) {
      a = -a, b = -b; }
		extend_gcd(a, L, t0, k0);
		//d是gcd(a,L)
		ll d = a * t0 + L * k0;
		//下面的看博客,对我这种新手有点难度
		if (b % d != 0) {
     
			cout << "Impossible" << endl;
			continue;
		}
		//这里不懂可以再去看看扩展欧几里得如何得到最小解答
		cout << (t0 * (b / d)%(L/d) +(L/d)) % (L/d) << endl;
	}
}

hdu1576 A/B

上面一题分析透彻,这一题只要自己理清题意,得到等式就可以利用欧几里得扩展了。
C数学基础专题_第8张图片

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b) {
     
	return b == 0 ? a : gcd(b, a % b);
}
ll lcm(ll a, ll b) {
     
	return a/ gcd(a, b) * b ;
}
void extend_gcd(ll B, ll mod, ll& k, ll& a) {
     
	if (mod == 0) {
     
		k = 1, a = 0;
		return;
	}
	extend_gcd(mod, B%mod, k, a);
	ll tmp = k;
	k = a;
	a = tmp - (B/mod) * a;
}
int main() {
     
	int N;
	cin >> N;
	while (N--) {
     
		ll n, B;
		cin >> n >> B;
		ll mod = 9973;
		ll k, a;
		//欧几里得扩展的前2个参数得是正数,后面2个算是x,y,可以为负数
		extend_gcd(B, mod, k, a);
		cout << ((k * n) % mod + mod) % mod << endl;
	}
}

乘法逆元&除法取模

hdu5976 Detachment

推荐博客:https://blog.csdn.net/weixin_41707869/article/details/90107418
C数学基础专题_第9张图片

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
int n;
const int MAXN = 1e5 + 5;
const int mod = 1e9 + 7;
ll mul[MAXN], sum[MAXN];
ll gcd(ll a, ll b) {
     
	return b == 0 ? a : gcd(b, a % b);
}
ll lcm(ll a, ll b) {
     
	return a/ gcd(a, b) * b ;
}
void extend_gcd(ll a, ll b, ll& x, ll& y) {
     
	if (b == 0) {
     
		x = 1, y = 0;
		return;
	}
	extend_gcd(b, a%b, x, y);
	ll tmp = x;
	x = y;
	y = tmp - (a/b) * y;
}
//逆元
ll mod_inverse(ll a, ll b) {
     
	ll x, y;
	extend_gcd(a, b, x, y);
	//防止负数
	return (x % b + b) % b;
}
int main() {
     
	int t;
	cin >> t;
	sum[1] = 0;
	mul[1] = 1;
	for (int i = 2;i <= MAXN-1;i++) {
     
		sum[i] = sum[i - 1] + i;
		mul[i] = mul[i - 1] * i % mod;
	}
	while (t--) {
     
		int n;
		//输入数据得用scanf或者快读,不然会超时
		scanf("%d",&n);
		if (n <= 4) {
     
			cout << n << endl;continue;
		}
		//upper_bound(begin,end,tar)在数列求种tar的上界,返回地址,所以减去sum+1
		ll k = upper_bound(sum + 1, sum +  MAXN, n)-sum-1;
		ll yu = n - sum[k];ll ans;
		if (yu == k) {
     
			ans = mul[k + 2] * mod_inverse(2, mod) % mod * mod_inverse(k + 1, mod)%mod;
		}
		else {
     
			ans = mul[k + 1] * mod_inverse(k +1- yu, mod) % mod;
		}
		printf("%lld\n", ans);
	}
}

素数筛选

线性欧拉筛大佬博客:https://www.cnblogs.com/liuwenyao/p/9931230.html

hdu1262 素数对

C数学基础专题_第10张图片

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int MAXN = 1e4 + 5;
bool visit[MAXN] = {
      0 };
int prime[MAXN];
int cnt=0;
//欧拉筛
void init() {
     
	for (int i = 2;i < MAXN;i++) {
     
		if (!visit[i])
			prime[++cnt] = i;
		for (int j = 1;j <= cnt, prime[j] * i < MAXN;j++) {
     
			visit[prime[j] * i] = true;
			if (i % prime[j] == 0)break;
		}
	}
}
int main() {
     
	init();
	int n;
	while (cin >> n) {
     
	//二分找第一个小于n/2的,然后看看另一个数是不是素数
		int k = upper_bound(prime + 1, prime + cnt + 1, n / 2)-(prime+1);
		//cout << k << endl;
		while (k) {
     
			if (visit[n - prime[k]] == false) {
     
				cout << prime[k] << " " << n - prime[k] << endl;
				break;
			}
			k--;
		}
	}
}

hdu2710 Max Factor

C数学基础专题_第11张图片

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int MAXN = 2e4 + 5;
bool visit[MAXN] = {
      0 };
int maxprime[MAXN];
int cnt=0;
//类似埃式筛
void init() {
     
//坑:1算质数,0也算质数
	maxprime[1] = 1;
	maxprime[0] = 0;
	for (int i = 2;i < MAXN;i++) {
     
	//遇到质数就更新所有比他大的合数,使得最大质因子为该数
		if (!maxprime[i]) {
     
			for (int j = i;j < MAXN;j += i)maxprime[j] = i;
		}
	}
}
int main() {
     
	init();
	int t;
	//多组输入,一个坑,有个坑数据 1 0 答案 0
	while (cin >> t) {
     
		int maxa = 0;
		int maxi = -1;
		for (int i = 0;i < t;i++) {
     
			int a;
			cin >> a;
			if (maxprime[a] > maxa) {
      maxa = maxprime[a]; maxi = a; }
		}
		cout << maxi << endl;
	}
}

hdu3826 Squarefree number

C数学基础专题_第12张图片

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int MAXN = 1e6 + 5;
bool visit[MAXN] = {
      0 };
int prime[MAXN];
int cnt=0;
//欧拉筛得到前1e6个数中的质数
void init() {
     
	for (int i = 2;i < MAXN;i++) {
     
		if (!visit[i])
			prime[++cnt] = i;
		for (int j = 1;j <= cnt && prime[j] * i < MAXN;j++) {
     
			visit[i * prime[j]] = true;
			if (i % prime[j] == 0)break;
		}
	}
}
int main() {
     
	init();
	int t;
	cin >> t;
	int k = 0;
	while (t--) {
     
		bool flag = false;
		long long n;
		cin >> n;
		//观察前面1e6个数的质数是否成为它的平方数
		for (int i = 1;i <= cnt&&prime[i]<=n;i++) {
     
			if (n % prime[i] == 0) {
     
				n /= prime[i]; 
				if (n % prime[i] == 0) {
     
					flag = true;
					break;
				}
			}
		}
		//如果在筛选后还大于1e6,那么只有1.素数 2.素数乘素数 3.素数的平方
		//最多是2次方,因为经过前面质数删选,1-1e6的质数都被删去,剩下的数就是大于1e6的素数,如果3次方,会大于1e18
		if (n > (long long)1e6) {
     
			long long a = sqrt(n);
			if (n == a * a)flag = true;
		}
		//注意格式
		cout << "Case " << ++k << ": ";
		if (flag)cout << "No" << endl;
		else cout << "Yes" << endl;
	}
}

组合数学

鸽巢原理

hdu1205 吃糖果

C数学基础专题_第13张图片

#include 
#include 
#define wbx 1000005
__int64 a[wbx];
#include 
//如果数量最多的糖果记为N,其余总和为s,如果是s>=N-1将数量最多的作为挡板,然后把所有排好序的糖果依次插入每个区域内,肯定不相邻
//如果s
int main()
{
     
	__int64 n,m,i;
    scanf("%I64d",&n);
	while(n--)
	{
     
		memset(a,0,sizeof(a));
		__int64 max=-99;
		__int64 sum=0;
		scanf("%I64d",&m);
        for(i=1;i<=m;i++)
		{
     
			scanf("%I64d",&a[i]);
			sum+=a[i];
			if(a[i]>max)
				max=a[i];
		}
		__int64 sum1=sum+1-max;
		if(sum1>=max)
			printf("Yes\n");
		else
			printf("No\n");
	}
	return 0;
}

hdu1808 Halloween treats

C数学基础专题_第14张图片


#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN = 1e5 + 5;
int num[MAXN];
int ans[MAXN];
int c, n;
int be, en;
void init() {
     
	memset(num, 0, sizeof num);
	memset(ans, 0, sizeof ans);
	be = -1, en = -1;
}
//抽屉原理,N个数,N-1个盒子,必然有2个数在一起
//把c作为N,因为n>=c,所以对于n个数求前缀和取余c,必然有2个相同
int main() {
     
	std::ios::sync_with_stdio(false);
	while (cin >> c >> n) {
     
		if (c == 0 && n == 0)break;
		init();
		map<int, int>m;
		ans[0] = 0;
		//如果前缀取余直接为0,那么就找到了,所以记m[0]为0
		m[0] = 0;
		for (int i = 1;i <= n;i++) {
     
			cin >> num[i];
		}
		for (int i = 1;i <= n;i++) {
     
			ans[i] = (ans[i - 1] + num[i])%c;
			if(m.find(ans[i])==m.end())
			m[ans[i]] = i;
			else {
     
				be = m[ans[i]], en = i;
				break;
			}
		}
		if (be == -1 && en == -1) {
     
			cout << "no sweets" << endl;
		}
		else {
     
			for (int i = be+1;i <= en-1;i++) {
     
				cout << i << " ";
			}
			cout << en << endl;
		}
	}
	
	
}

hdu5776 sum

C数学基础专题_第15张图片



#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN = 1e5 + 5;
int num[MAXN];
int ans[MAXN];
bool visit[MAXN];
int c, n;
void init() {
     
	memset(num, 0, sizeof num);
	memset(ans, 0, sizeof ans);
	memset(visit, 0, sizeof visit);
	ans[0] = 0;
	visit[0] = true;
}
//还是抽屉原理  
int main() {
     
	int t;
	cin >> t;
	while (t--) {
     
		init();
		bool flag = false;
		int n, m;
		cin >> n >> m;
		for (int i = 1;i <= n;i++) {
     
			cin >> num[i];
		}
		for (int i = 1;i <= n;i++) {
     
			ans[i] = (ans[i - 1] + num[i])%m;
			if (visit[ans[i]] == true) {
     
				flag = true;
				break;
			}
			else {
     
				visit[ans[i]] = true;
			}
		}
		if (flag == true)
			cout << "YES" << endl;
		else cout << "NO" << endl;
	}
}

容斥原理

hdu2841 Visible Trees

C数学基础专题_第16张图片

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 5;
int prime[MAXN];
int cnt = 0;
//这题说白了就是在m,n中遍历每个点,如果x,y互质那么就可以看见
//getp得到素数
void getp(int n) {
     
	cnt = 0;
	for (int i = 2;i * i <= n;i++) {
     
		if (n % i == 0) {
     
			prime[cnt++] = i;
			while (n % i == 0)n /= i;
		}
	}
	if (n > 1)prime[cnt++] = n;
}
//容斥原理,读者看不懂,可以看一下容斥原理三集合,然后自己往后推理
ll getf(ll m) {
     //求不互质的数
	int t = 0;
	ll que[1005];
	//这里定-1为了后面ans+=计算方便
	que[t++] = -1;
	for (int i = 0;i < cnt;i++) {
     
		int k = t;
		for (int j = 0;j< k;j++) {
     
		//读者可以定一个5*5的图,自己看,这里每一次计算,都相当于几个质数相乘,而正负则表明在容斥公式中的符号
		//容斥定理求集合,先得加上所有单个子集,然后减去所有2个子集得交集,然后加上所有3个子集得交集。。。等等往后推
		//这里每个quei都是prime数组相乘结果,prime相乘就相当于交集了,比如质数2,3,相乘为6,那么我们在2,3基础上减去6的倍数,就是减去交集
			que[t++] = (-1) * que[j] * prime[i];
		}
	}
	ll ans = 0;
	for (int i = 1;i < t;i++) {
     
	//这里m/que[i]表示que[i]的倍数,因为que[i]都是prime相乘的结果,如果m=12,que=2,那么一除,表示有6个2的倍数,que=3的话,一除,表示4个3的倍数,同理往后。
		ans += (m / que[i]);
	}
	return ans;
}
int main() {
     
	int t;
	ll a, b,res;
	scanf_s("%d", &t);
	while (t--) {
     
		scanf_s("%lld%lld", &a, &b);
		if (a > b) {
     
			ll tmp = a;
			a = b;
			b = tmp;
		}
		res = 0;
		for (int i = 1;i <= a;i++) {
     
			getp(i);
			//这里读者不妨自己推理为何要b减去,因为我们getf得到的是反过来的,是不互斥的总集合
			res += (b - getf(b));
		}
		printf("%lld\n",res);
	}
}

hdu4135 Co-prime

C数学基础专题_第17张图片


#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 5;
ll prime[MAXN];
ll que[MAXN];
int cnt = 0;
void getp(ll n) {
     
	cnt = 0;
	for (ll i = 2;i * i <= n;i++) {
     
		if (n % i == 0) {
     
			prime[cnt++] = i;
			while (n % i == 0)n /= i;
		}
	}
	if (n > 1)prime[cnt++] = n;
}
ll getf(ll m) {
     
	int t = 0;
	que[t++] = -1;
	for (int i = 0;i < cnt;i++) {
     
		int k = t;
		for (int j = 0;j< k;j++) {
     
			que[t++] = (-1) * que[j] * prime[i];
		}
	}
	ll ans = 0;
	for (int i = 1;i < t;i++) {
     
		ans += (m / que[i]);
	}
	return ans;
}
int main() {
     
	int t;
	ll a, b,n,res;
	int k = 0;
	scanf_s("%d", &t);
	while (t--) {
     
		scanf_s("%lld%lld%lld", &a, &b,&n);
		res = 0;
		getp(n);
		//b-getf(b),求前b的互质的数,再减去前a-1的互质的数
		res = (b - getf(b))-(a-1-getf(a-1));
		cout << "Case #" << ++k << ": ";
		printf("%lld\n",res);
	}
}

斐波那契数列

hdu3117 Fibonacci Numbers

poj3070 Fibonacci

都是求大数斐波那契数列,hdu3117上面已经写过,用到矩阵快速幂。

母函数

hdu1028 Ignatius and the Princess III

普通母函数用于求解组合方案数
C数学基础专题_第18张图片


#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN = 125;
int  c1[MAXN + 1], c2[MAXN + 1];
//母函数模板题,读者自己找关于母函数的定义
void init() {
     
//按照正常思路展开母函数,先对系数初始化,c2记录临时变量
	for (int i = 0;i <= MAXN;i++) {
     
		c1[i] = 1;
		c2[i] = 0;
	}
	//i表示第i个多项式,k+=i,表示x的幂每次递增i
	for (int i = 2;i <= MAXN;i++) {
     
	//j表示从相乘2个多项式中左边多项式的关于x的幂
		for (int j = 0;j <= MAXN;j++) {
     
		//k表示2个多项式中右边的关于x的幂
			for (int k = 0;k + j <= MAXN;k += i) {
     
			//2个幂相乘,得到j+k的幂,为何+=,因为c1存的相当于是系数,c1[j]表示x的j次幂的系数
				c2[j + k] += c1[j];
			}
		}
		//每次乘完2个多项式都要把c1和c2转化一下
		for (int i = 0;i <= MAXN;i++) {
     
			c1[i] = c2[i];
			c2[i] = 0;
		}
	}
}
int main() {
     
	int n;
	init();
	while (cin >> n) {
     
		cout << c1[n] << endl;
	}
}

hdu1521 排列组合

指数型母函数,用于求排列
C数学基础专题_第19张图片


#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN = 15;
double  c1[MAXN + 1], c2[MAXN + 1];
double jiec[MAXN + 1];
int num[MAXN];
int n, m;
void getjiec() {
     
	jiec[0] = 1.0;
	jiec[1] = 1;
	for (int i = 2;i <= MAXN;i++) {
     
		jiec[i] = i * 1.0 * jiec[i - 1];
	}
}

int main() {
     
	getjiec();
	while (cin >> n >> m) {
     
		for (int i = 1;i <= n;i++) {
     
			cin >> num[i];
		}
		memset(c1, 0, sizeof(c1));
		memset(c2, 0, sizeof(c2));
		//初始化第一个多项式,作为乘法的左边的数
		for (int i = 0;i <= num[1];i++) {
     
			c1[i] = 1.0/jiec[i];
			c2[i] = 0;
		}
		//从第二个多项式开始分别和第一个多项式乘
		for (int k = 2;k <= n;k++) {
     
			for (int i = 0;i <= m;i++) {
     
			//多了限制条件
				for (int j = 0;j <= num[k] && j + i <= m;j++) {
     
					c2[j + i] += c1[i] / jiec[j];
				}
			}
			for (int i = 0;i <= m;i++) {
     
				c1[i] = c2[i];
				c2[i] = 0;
			}
		}
		printf("%.0lf\n", c1[m] * jiec[m]);
	}

}

特殊计数

hdu2067 小兔的棋盘(卡特兰数)

C数学基础专题_第20张图片


#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN = 36;
ll c[MAXN];
//卡特兰数的代码
//虽说有点抽象,但是就相当于给你一堆1和0,让你整理一个数列使得对任意前k项,1的数目大于或等于0的数目。
//这题就是把向上走比作0,向右走比作1,向右的数目必须大于等于0,不然就不是。
//同理,对判断括号是否合理比如()()合理,(()不合理,也是一样,把左括号比作1,右括号比作0.
void init() {
     
	c[0] = 1;
	for (int i = 1;i <= 35;i++) {
     
		for (int j = 0;j < i;j++) {
     
			c[i] += c[j] * c[i - j-1];
		}
	}
}
int main() {
     
	init();
	int n;
	int k = 0;
	while (cin >> n) {
     
		if (n == -1)break;
		cout << ++k << " " << n << " " << 2 * c[n]<<endl;
	}
}

令h(0)=1,h(1)=1,catalan数满足递推式[1]:

h(n)= h(0)*h(n-1)+h(1)*h(n-2) + … + h(n-1)h(0) (n>=2)

例如:h(2)=h(0)h(1)+h(1)h(0)=11+11=2

h(3)=h(0)h(2)+h(1)h(1)+h(2)h(0)=12+11+21=5

另类递推式[2]:

h(n)=h(n-1)(4n-2)/(n+1);

递推关系的解为:

h(n)=C(n,2n)*(2n-1) (n=0,1,2,…)

递推关系的另类解为:

h(n)=c(2n,n)-c(2n,n+1)(n=0,1,2,…)

hdu4372 Count the Buildings( 斯大林数第一类)

C数学基础专题_第21张图片

hdu2643 Rank(斯大林数第二类)

C数学基础专题_第22张图片

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN =105;
const int mod = 20090126;
ll c[MAXN][MAXN];
ll s[MAXN][MAXN];
ll jiec[MAXN];
void init() {
     
	s[0][0] = 1;
	jiec[0] = 1;
	//初始化得到阶乘
	for (int i = 1;i <= MAXN - 1;i++) {
     
		jiec[i] = jiec[i - 1] * i % mod;
		s[i][0] = 0;
	}
	//得到斯大林数
	for (int i = 1;i <= MAXN - 1;i++) {
     
		for (int j = 1;j <= MAXN - 1;j++) {
     
			s[i][j] = (s[i - 1][j - 1] + j * s[i - 1][j]%mod)%mod;
		}
	}
}
int main() {
     
	init();
	int t;
	scanf_s("%d", &t);
	while (t--) {
     
		int n;
		scanf_s("%d", &n);
		ll ans = 0;
		//把平局的人装进盒子,不平局的看成不同盒子。
		for (int i = 1;i <= n;i++) {
     
			ans =(ans+ s[n][i] * jiec[i] )% mod;
		}
		printf("%lld\n", ans);
	}
}



公平组合游戏

巴什游戏

hdu1846 Brave Game

C数学基础专题_第23张图片


#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
int main() {
     
	int t;
	cin >> t;
	while (t--) {
     
		int n, m;
		cin >> n >> m;
		//一个人最多去m颗,所以如果石头是m+1倍数的话,甲无论取几颗,乙取到m+1颗就行
		if (n % (m + 1) == 0)cout << "second" << endl;
		else cout << "first" << endl;
	}
}


必胜点N必败点P与动态规划

hdu2147 kikis game

C数学基础专题_第24张图片


#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
int main() {
     
	int n, m;
	while (cin >> n >> m) {
     
		if (n == 0 && m == 0)break;
		//读者自己画图找一下规律,对于左下角3个状态,有P为N,全N为P
		if (n % 2 == 0) {
     
			cout << "Wonderful!" << endl;
			continue;
		}
		else {
     
			if (m % 2 == 0)cout << "Wonderful!" << endl;
			else cout << "What a pity!" << endl;
		}
	}
}




尼姆游戏

hdu1850 Being a Good Boy in Spring Festival

C数学基础专题_第25张图片


#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN = 105;
int a[105];
//尼姆游戏:n堆,每次取任意一堆的任意个石子,用异或运算
int main() {
     
	int n;
	while (cin >> n) {
     
		if (n == 0)break;
		int sum = 0, ans = 0;
		for (int i = 1;i <= n;i++) {
     
			cin >> a[i];
			sum ^= a[i];
		}
		if (sum == 0)cout << 0 << endl;
		else {
     
			for (int i = 1;i <= n;i++) {
     
			//不等式左边表示除了a[i]其他的异或和,如果小于右边,就把右边的数拿到和左边一样
				if ((sum ^ a[i]) < a[i])ans++;
			}
			cout << ans << endl;
		}
	}
}



SG函数

大佬博客:https://www.cnblogs.com/graytido/p/10771907.html

hdu1848 Fibonacci again and again

C数学基础专题_第26张图片


#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN = 1005;
int sg[1005],s[1005];
int f[15] = {
      1,2,3,5,8,13,21,34,55,89,144,233,377,610,987 };
void getsg() {
     
	sg[0] = 0;
	sg[1] = 0;
	for (int i = 1;i <= 1000;i++) {
     
		sg[i] = i;
		memset(s, 0, sizeof(s));
		for (int j = 0;j < 15 && f[j] <= i;j++) {
     
			s[sg[i - f[j]]] = 1;
		}
		for (int j = 0;j <= i;j++) {
     
			if (s[j] == 0) {
     
				sg[i] = j;break;
			}
		}
	}
}
int main() {
     
	getsg();
	int n, m, p;
	while (cin >> n >> m >> p) {
     
		if (n == 0 && m == 0 && p == 0)break;
		if ((sg[n] ^ sg[m] ^ sg[p] )== 0)cout << "Nacci" << endl;
		else cout << "Fibo" << endl;
	}
}



威佐夫游戏

hdu1527 取石子游戏

这里分析奇异局势与非奇异局势的转换。首先奇异局势在这里是P(必败),那么我们处在奇异局势时,必败,我们处在非奇异时,能不能转到奇异局势,见大佬博客:https://blog.csdn.net/Jason_crawford/article/details/52129969?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param
我们可以知道非奇异能够成为奇异,换句话说,因为奇异时P,我们非奇异能变为P,那么我们非奇异就是N(必胜)
C数学基础专题_第27张图片


#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN = 1005;
//代码实现
int main() {
     
	int n, m;
	while (cin >> n >> m) {
     
		int a = max(n, m);
		int b = min(n, m);
		double k = (double)(a - b);
		double gold = (1 + sqrt(5)) / 2;
		int test = (int)(k * gold);
		if (test == b)cout << 0 << endl;
		else cout << 1 << endl;
	}
}


你可能感兴趣的:(算法入门,算法)