求模相关
主要来自洛谷,作记录用
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;
}