数学题

求模相关

主要来自洛谷,作记录用
P3951
找规律

#include <iostream>
#include <string>
#include<cmath>
#include <cstdlib>
#include <queue>
#include <stack>
#include<vector>
#include"string.h"
#include"string"
#include <map>
#include <sstream>
#include <iomanip>
#include <algorithm>
#define INF 0x7fffffff
#define lson num << 1
#define rson num << 1 | 1
using namespace std;
const int  maxn = 1e3 + 5;
long long  a, b,ans;
int main() {
	cin >> a >> b;
	ans = b * (a - 1) - a;
	cout << ans << endl;
	return 0;
}

P4942
刚开始是完全打表找规律的,
发现l+…+r=res,res的每一个数字的和加起来%9就是答案,然后,longlong爆了。
于是,原来需要乘法逆元啊
模板get

#include <iostream>
#include <string>
#include<cmath>
#include <cstdlib>
#include <queue>
#include <stack>
#include<vector>
#include"string.h"
#include"string"
#include <map>
#include <sstream>
#include <iomanip>
#include <algorithm>
#define LL long long 
#define INF 0x7fffffff
#define lson num << 1
#define rson num << 1 | 1
using namespace std;
const int  maxn = 1e3 + 5;
long long  l, r;
int n;
int inv[maxn];
int a, g, x, y,p=9;
LL exgcd(LL a, LL b, LL &x, LL &y)
//扩展欧几里得算法 
{
	if (b == 0)
	{
		x = 1, y = 0;
		return a;
	}
	LL ret = exgcd(b, a%b, y, x);
	y -= a / b * x;
	return ret;
}
LL getInv(int a, int mod)//求a在mod下的逆元,不存在逆元返回-1 
{
	LL x, y;
	LL d = exgcd(a, mod, x, y);
	return d == 1 ? (x%mod + mod) % mod : -1;

}


int main() {
//	cout << getInv(2, 9) << endl;
	cin >> n;
	while (n--) {
		cin >> l >> r;
		long long ans = (l + r) % 9 * (r - l + 1) % 9 * 5 % 9;
		cout << ans << endl;
	}
	return 0;
}

P3811板子 求乘法逆元
O(N)算法,题解区有宝藏

#include <iostream>
#include <string>
#include<cmath>
#include <cstdlib>
#include <queue>
#include <stack>
#include<cstdio>
#include<vector>
#include"string.h"
#include"string"
#include <map>
#include <sstream>
#include <iomanip>
#include <algorithm>
#define LL long long 
#define INF 0x7fffffff
#define lson num << 1
#define rson num << 1 | 1
using namespace std;
const int  maxn = 3e6 + 5;
int n, p,x,y;
/*
LL exgcd(LL a, LL b, LL &x, LL &y)
//扩展欧几里得算法 
{
	if (b == 0)
	{
		x = 1, y = 0;
		return a;
	}
	LL ret = exgcd(b, a%b, y, x);
	y -= a / b * x;
	return ret;
}
void exgcd(int a, int b, int& x, int & y) {
	if (!b) {
		x = 1, y = 0;return;
	}
	exgcd(b, a%b, x, y);
	int z = x;x = y;
	y = z - a/b*x;
}*/
int inv[maxn];
void work() {
	inv[1] = 1;
	for (int i = 2; i <= n; i++) 
		inv[i] = (LL)(p -p / i )* inv[p % i] % p;
}
/*
LL getInv(int a, int mod)//求a在mod下的逆元,不存在逆元返回-1 
{
	LL x, y;
	LL d = exgcd(a, mod, x, y);
	return d == 1 ? (x%mod + mod) % mod : -1;

}
*/
int main() {
	scanf("%d %d",&n,&p);
	work();
	for (int i = 1;i <= n;i++) {
			printf("%d\n", inv[i]); 
	
	}
	return 0;
}

P1516 &P1082
差不多,都是扩展欧几里得的推导
P1516



#include"pch.h"

#include<cstdio>
#include <iostream>
#include <string>
#include <cstdlib>
#include <queue>
#include <stack>
#include<vector>
#include"string.h"
#include"string"
#include <map>
#include <sstream>
#include <iomanip>
#include <algorithm>
#define INF 0x7fffffff
#define lson num << 1
#define rson num << 1 | 1
#define ll long long
using namespace std;
const int  maxn = 1e3 + 5;
ll x00, y00, m, n, L,x,y;
void exgcd(ll a, ll b, ll &x, ll &y) {
	if (b == 0) {
		 x = 1, y = 0;
		 return;
	}
	exgcd(b, a%b, y, x);
	y -= (a / b)*x;
}
ll gcd(ll a, ll b) {
	return b == 0 ? a : gcd(b, a%b);
}

int main() {
	scanf_s("%lld %lld %lld %lld %lld", &x00, &y00,&m,&n,&L);

	//保证是正整数。
	ll gcd_ = gcd(L, abs(m - n));
	ll mn = m - n;
	ll xy = y00 - x00;
	if (mn < 0) {
		mn = -mn;
		xy = -xy;
	}
	if (xy % gcd_ != 0) cout << "Impossible" << endl;
	else {
		exgcd(mn, L, x, y);
		//cout << gcd_ << endl;
		//忘了输入了,我是弱智
		ll mod =(ll)L / gcd_;
		//cout << mod << endl;
		// (x%mod+mod)%mod防止裂开
		//cout << x << endl;
		cout << (x * xy / gcd_ % mod + mod) % mod << endl;
	}
	return 0;
}


记录一下推导过程:

P1516:

已知x0,y0,m,n,L为正整数,满足:
x0 + mz = y0 + nz (mod L)
求Z的最小值(z为正整数)
x0 + mz = y0 + nz (mod L)

解答:

(m-n)z = y0-x0 (mod L)
所以有解的条件为:
(y0-x0)% gcd(L, m-n) =0, 即为(y0-x0)能被gcd(L,m-n)整除。

  • 证明:令m-n = a, z = x(未知数), y0-x0 = b
  • 原式可表示为ax = b (mod L)
  • so 可以设 ax = kL + b
  • ax - kL = b
  • 由gcd的定义得到 a整除gcd(a,L), L整除gcd(a,L),所以原式可写为
  • gcd(a,L)*(a/gcd(a,L)*x + c/gcd(a,L)) = b
  • 若x有整数解,那么b/gcd(a,L)必定为整数
  • 综上方程ax = b (mod L)有整数解的必要条件为b%gcd(a,L)=0

回到刚才,a = m-n, b = x0 - y0
(1)所以有解条件为(y0-x0)/gcd(L,m-n),若不满足,直接输出impossible
(2)若满足,继续往下。
a x = b (mod L)
ax%L = b%L
ax - b = yL
ax - yL = b
即为(m-n)x - Ly = b = y0 - x0
因为(y0-x0)%gcd(L, m-n) =0,设(y0-x0) = t * gcd(L, m-n)
代入得到
(m-n)x + L (-y) = t * gcd(L, m-n)
(m-n)*(x / t) + L (-y / t) = gcd(L,m-n)
令x’ = x / t, y’ = -y / t
得到扩展欧几里得方程
(m-n)
x’ + L *y’ = gcd (L, m-n)
用模板求解得到x’, 而答案为x = x’ * t = x’ * ((y0-x0) / gcd(L ,m-n))

  • 另外,因为gcd模板(我的)只能处理正整数,而m-n未必是正整数,
  • 当m-n<0时,取相反数,此时记得在y0-x0上也对应取相反数。

P1082:

没有题意,告辞
求ax = 1(mod b)的解

解答:

根据上文的推导
ax = 1(mod b)有解的条件为gcd(a, b)=1
而题目保证有解,那么 gcd(a,b)=1
ax % b = 1 %b
(ax -1) = kb
ax - kb = 1 = gcd(a,b)
扩展欧几里德,安排。
ans = x

扩展欧几里德递归求法的小推导

扩展欧几里德用来解方程:
ax + by = gcd(a, b)
a = [a/b] * b + a%b
由欧几里德 : gcd(a ,b) = gcd(b, a%b)
([a/b]b + a%b)x + by =gcd(a,b) = gcd(b, a%b)
b ( [a/b] x + y) + (a%b) x = gcd(b, a%b)
令x’ = [a/b] x + y , y’ = x , a’ = b, b’ = a%b
则 a’
x’ + b’
y’ = gcd(a’ , b’)
所以exgcd函数中有 Y -= x *[a/b],交换x,y的操作

中国剩余定理&扩展中国剩余定理

P3868 猜数字
中国剩余定理



#include"pch.h"

#include<cstdio>
#include <iostream>
#include <string>
#include <cstdlib>
#include <queue>
#include <stack>
#include<vector>
#include"string.h"
#include"string"
#include <map>
#include <sstream>
#include <iomanip>
#include <algorithm>
#define INF 0x7fffffff
#define lson num << 1
#define rson num << 1 | 1
#define ll long long
using namespace std;
const int  maxn = 1e6 + 5;
int n;
ll a[20], b[20];
ll mul = 1;

ll read()
{
	ll f = 1, x = 0;
	char ss = getchar();
	while (ss<'0' || ss>'9') { if (ss == '-')f = -1;ss = getchar(); }
	while (ss >= '0'&&ss <= '9') { x = x * 10 + ss - '0';ss = getchar(); }
	return x * f;
}

void exgcd(ll a, ll b, ll &x, ll &y) {
	if (b == 0) { x = 1;y = 0;return; }
	exgcd(b, a%b, y, x);
	y -= a / b *x;
}

ll ksc(ll a, ll b, ll mod) {
	ll res = 0;
	while (b>0) {
		if (b & 1) {res = (res + a) % mod;}
		a = (a + a) % mod;
		b >>= 1;
	}
	return res;
 }

ll crt() {
	ll res = 0,x =0,y=0;
	for (int i = 1;i <= n;i++) {
		ll cur = mul / b[i];
		exgcd(cur, b[i], x, y);
		x = (x%b[i] + b[i]) % b[i];
		res = (res + ksc(ksc(cur,x,mul),a[i],mul)) % mul;
	}
	return (res+mul)%mul;
}

int main() {
	n = read();
	for (int i = 1;i <= n;i++) {
		a[i] = read();
	}
	for (int i = 1;i <= n;i++) {
		b[i] = read();
		mul *= b[i];
		a[i] = (a[i] % b[i] + b[i]) % b[i];
	}
	cout << crt();
	return 0;
}

P4777 扩展中国剩余定理
去掉了两两互素的条件

#include<cstdio>
#include <iostream>
#include <string>
#include <cstdlib>
#include <queue>
#include <stack>
#include<vector>
#include"string.h"
#include"string"
#include <map>
#include <sstream>
#include <iomanip>
#include <algorithm>
#define INF 0x7fffffff
#define lson num << 1
#define rson num << 1 | 1
#define ll long long
using namespace std;
const int  maxn = 1e6 + 5;
int n;
ll a[maxn], b[maxn];

//返回的a,就是最大公因数了
ll exgcd(ll a, ll b, ll &x, ll &y) {
	if (b == 0) {
		x = 1;y = 0;return a;
	}
	ll gcd = exgcd(b, a%b, y, x);
	y -= a / b *x;
	return gcd;
}

ll mul(ll a, ll b, ll mod) {
	ll res = 0;
	while (b) {
		if (b & 1) {
			res = (res + a) % mod;
		}
		a = (a * 2) % mod;
		b >>= 1;
	}
	return res;
 }

ll excrt() {
	ll x=0, y=0, k;
	//n=1
	ll M = b[1], ans = a[1];
	
	for (int i = 2;i <= n;i++) {
		ll A = M, B = b[i], C = (a[i] - ans % B + B) % B;
		// Ax = C (mod B)
		//AB的gcd
		ll A_B_gcd = exgcd(A, B, x, y);
		ll mod = B / A_B_gcd;
		//有解条件
		if (C%A_B_gcd != 0) return -1;
		x = mul(x, C / A_B_gcd, mod);
		ans += x * M;
		M *= mod;
		ans = (ans%M + M) % M;
	}
	return ans;
}

int main() {
	cin >> n;
	for (int i = 1;i <= n;i++) {
		cin >> b[i] >> a[i];
	}
	cout<<excrt()<<endl;
	return 0;
}


计数相关

P2525
虽然是STL的大水题,
prev_permutation(a + 1, a + 1 + n);
学了另一种好办法,但复杂度不太ok
康托尔展开&逆康托尔展开

(洛谷好像用不了emplace.back() ?)

#include<cstdio>
#include <iostream>
#include <string>
#include <cstdlib>
#include <queue>
#include <stack>
#include<vector>
#include"string.h"
#include"string"
#include <map>
#include <sstream>
#include <iomanip>
#include <algorithm>
#define INF 0x7fffffff
#define lson num << 1
#define rson num << 1 | 1
#define ll long long
using namespace std;
const int  maxn = 1e3 + 5;
int a[maxn];
int n;
int cal(int x) {
	int res = 1;
	for (int i = 2;i <= x;i++) {
		res *= i;
	}
	return res;
}
//求得排名
int cantor() {
	int res = 0;
	for (int i = 1;i <= n;i++) {
		int cnt = 0;
		for (int j = i + 1;j <= n;j++) {
			if (a[j] < a[i]) cnt++;
		}
		res += cal(n - i) *(cnt);
	}
	return res;
}
//排名-1
void incantor(int id) {
	vector<int> v;
	if (!v.empty()) v.clear();
	for (int i = 1;i <= n;i++)v.emplace_back(i);
	for (int i = 1;i < n;i++) {
		int tot_kind = cal(n-i);
		int cur_id = id / tot_kind;
		a[i] = v[cur_id];
		v.erase(v.begin() + cur_id);
		id %= tot_kind;
	}
	//其实这里很妙啊,v是从0编号,而a是从1编号,正好id/tot_kind取整,而要进行+1的操作
	// 正好a[1-n]=v[0-n-1]暗中加1
	//每次计算后,去掉这个数字,不影响后面的排序判断
	//一开始就是按序的,取出后,也是递增的
	a[n] = v[0];
	return;
}
int main() {
	cin >> n;
	for (int i = 1;i <= n;i++) {
		cin >> a[i];
	}
	incantor(cantor() - 1);
	for (int i = 1;i <= n;i++) {
		cout << a[i] << " ";
	}
	return 0;
}


P1287盒子与球
递推! 感觉应该蛮经典的吧
Stirling数。



#include"pch.h"

#include<cstdio>
#include <iostream>
#include <string>
#include <cstdlib>
#include <queue>
#include <stack>
#include<vector>
#include"string.h"
#include"string"
#include <map>
#include <sstream>
#include <iomanip>
#include <algorithm>
#define INF 0x7fffffff
#define lson num << 1
#define rson num << 1 | 1
#define ll long long
using namespace std;
const int  maxn = 1e3 + 5;
ll f[maxn],ans;
int n, r;
ll fac(ll x) {
	ll res = 1;
	for (int i = 2;i <= x;i++) res *= i;
	return res;
}
//m个盒子,放n个球
//如果第i个单独放,后面n-1个球,放到m-1个盒子
//如果不单独放,那么后面n-1个球,放到m个盒子里,可以随这些球进入任何盒子*m
ll cal(int m,int n) {
	if (n < m || m < 0) return 0;
	if (n == m) return 1;
	return cal(m - 1, n - 1) + cal(m, n - 1)*m;
}

int main() {
	cin >> n >> r;
	cout << cal(r, n)*fac(r) << endl;
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值