整理的算法模板合集: ACM模板
点我看算法全家桶系列!!!
实际上是一个全新的精炼模板整合计划
繁凡出品的全新系列:解题报告系列 —— 超高质量算法题单,配套我写的超高质量的题解和代码,题目难度不一定按照题号排序,我会在每道题后面加上题目难度指数( 1 ∼ 5 1 \sim 5 1∼5),以模板题难度 1 1 1 为基准。
这样大家在学习算法的时候就可以执行这样的流程:
%
阅读【学习笔记】 / 【算法全家桶】学习算法 ⇒ \Rightarrow ⇒ 阅读相应算法的【解题报告】获得高质量题单 ⇒ \Rightarrow ⇒ 根据一句话题解的提示尝试自己解决问题 ⇒ \Rightarrow ⇒ 点开详细题解链接学习巩固(好耶)
%
要是26个英文字母用完了我就接上24个希腊字母,我就不信50道题不够我刷的hhh
%
解题报告系列合集:【解题报告系列】超高质量题单 + 题解(ICPC / CCPC / NOIP / NOI / CF / AT / NC / P / BZOJ)
本题单前置知识:《算法竞赛中的初等数论》(二)正文 0x20同余(ACM / OI / MO)(十五万字符数论书)
Weblink
https://www.luogu.com.cn/problem/P3868
Problem
Solution
b i ∣ ( n − a + i ) n − a i % b i = 0 n − a i ≡ 0 ( m o d b i ) n ≡ a i ( m o d b i ) \begin{aligned} & b_i\ |\ (n - a+i) \\ &n - a_i\%b_i=0\\&n-a_i\equiv0(\mod b_i)\\&n\equiv a_i(\mod b_i)\end{aligned} bi ∣ (n−a+i)n−ai%bi=0n−ai≡0(modbi)n≡ai(modbi)
显然有 n n n 个同余方程,直接CRT解方程组即可。
注意数据保证 ∏ i = 0 k b i ≤ 1 0 18 \prod\limits_{i=0}^{k}b_i\le 10^{18} i=0∏kbi≤1018,即 M ≤ 1 0 18 M\le 10^{18} M≤1018,那么CRT的过程中随便一乘就会爆 long long ,所以要用快速乘。用 l o g n logn logn 的快速乘竟然 T 了…所以需要加一些经典优化或者用 O ( 1 ) O(1) O(1) 的快速乘,可以处理小于 1.7 × 1 0 308 1.7\times 10^{308} 1.7×10308 的数据
Code
#include
#include
#include
#include
#include
#include
using namespace std;
#define int long long
typedef long long ll;
typedef int itn;
typedef pair<int, int> PII;
typedef pair<double, int> PDI;
const int N = 50 + 7, mod = 1e18 + 7, INF = 0x3f3f3f3f;
const double PI = acos(-1.0), eps = 1e-8;
int n, t, kcase;
int M, m[N], a[N], k;
/*
int mul(int a, int b, int mod)
{
int res = 0;
while(b) {
if(b & 1) res = (res + a) % mod;
a = (a + a) % mod;
b >>= 1;
}
return res;
}*/
int mul(int x, int y, int p)
{
int z = (long double)x / p * y;
ll res = (unsigned long long)x * y - (unsigned long long) z * p;
return (res + p) % p;
}
ll exgcd(int a, int b, int &x, int &y)
{
if(b == 0) {
x = 1, y = 0;
return a;
}
ll d = exgcd(b, a % b, x, y);
ll z = x;
x = y;
y = z - y * (a / b);
return d;
}
void solve()
{
ll M = 1;
scanf("%lld", &k);
for(int i = 1; i <= k; ++ i) {
scanf("%lld", &a[i]);
}
for(int i = 1; i <= k; ++ i) {
scanf("%lld", &m[i]);
M *= m[i];
}
for(int i = 1; i <= k; ++ i) a[i] = (a[i] % m[i] + m[i]) % m[i];
ll res = 0;
for(int i = 1; i <= k; ++ i) {
ll Mi = M / m[i];
ll ti, y;
ll d = exgcd(Mi, m[i], ti, y);
ti = (ti % m[i] + m[i]) % m[i];
res += mul(mul(a[i], ti, M), Mi, M);
}
printf("%lld\n", (res % M + M) % M);
}
signed main()
{
solve();
return 0;
}
Weblink
https://www.luogu.com.cn/problem/P2480
Problem
Solution
void 函数写成 ll 了,没有返回值 RE 了…
经典数论全家桶
懒得写题解了…
Code
简单实现一下就行了
#include
using namespace std;
typedef long long ll;
typedef int itn;
const ll N = 5e5 + 7, p = 999911659, phi = 999911658, INF = 0x3f3f3f3f;
const double PI = acos(-1.0), eps = 1e-8;
ll val;
ll a[10];
ll M = 1;
ll fact[N];
ll infact[N];
ll n, g, t, cnt;
ll mo[10] = {
0, 2, 3, 4679, 35617};
ll qpow(ll a, ll b, ll mod)
{
ll res = 1;
while(b) {
if(b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
ll exgcd(ll a, ll b, ll &x, ll &y)
{
if(b == 0) {
x = 1, y = 0;
return a;
}
ll d = exgcd(b, a % b, x, y);
ll z = x;x = y, y = z - y * (a / b);
return d;
}
void init(ll p)
{
fact[0] = infact[0] = 1;
for(ll i = 1; i < p; ++ i) {
fact[i] = fact[i - 1] * i % p;
infact[i] = infact[i - 1] * qpow(i, p - 2, p) % p;
}
}
ll C(ll a, ll b, ll p)
{
if(a < b) return 0;
return fact[a] * infact[b] % p * infact[a - b] % p;
}
ll lucas(ll a, ll b, ll p)
{
if(b == 0) return 1;
if(a < p && b < p) return C(a, b, p);
return C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;
}
ll CRT()
{
ll res = 0;
for(ll i = 1; i <= 4; ++ i) {
ll Mi = M / mo[i];
ll ti, y;
ll d = exgcd(Mi, mo[i], ti, y);
ti = (ti % mo[i] + mo[i]) % mo[i];
res = (res + a[i] * ti % M * Mi % M) % M;
}
return (res % M + M) % M;
}
void solve()
{
M = phi;
scanf("%lld%lld", &n, &g);
if(g % p == 0) {
puts("0");
return ;
}
for(ll k = 1; k <= 4; ++ k) {
init(mo[k]);
for(ll i = 1; i * i <= n; ++ i) {
if(n % i == 0) {
a[k] = (a[k] + lucas(n, i, mo[k])) % mo[k];
if(i * i != n)
a[k] = (a[k] + lucas(n, n / i, mo[k])) % mo[k];
}
}
}
printf("%lld\n", qpow(g, CRT(), p));
}
int main()
{
solve();
return 0;
}
Weblink
https://www.luogu.com.cn/problem/P4774
Problem
Solution
可以直接用 multiset
代替平衡树,计算出每条龙所需要的剑,并且这把剑是一定能打败这条龙的,不然最后方程组无解,就会输出 -1,所以我们直接把打败龙的奖励放入 set 里,再选下一把剑。显然题目中的恢复机制我们可以得到一个同余方程:
C i x ≡ A i ( m o d P i ) \begin{aligned}&C_ix\equiv A_i(\mod P_i)\end{aligned} Cix≡Ai(modPi)
题目数据不保证 m[i]
一定互质 ⇒ \Rightarrow ⇒ excrt
一般的同余方程为:
x ≡ A i ( m o d P i ) \begin{aligned}&x\equiv A_i(\mod P_i)\end{aligned} x≡Ai(modPi)
可以直接用拓展中国剩余定理。
但是本题的式子长这个样子:
C i x ≡ A i ( m o d P i ) \begin{aligned}&C_ix\equiv A_i(\mod P_i)\end{aligned} Cix≡Ai(modPi)
考虑转成标准式子
C i x ≡ A i ( m o d P i ) 显然有 C i x + P i y = A i exgcd解得 x’ y’ x = x ′ + k b d = x ′ + k P i gcd ( P i , C i ) ( m o d P i gcd ( P i , C i ) ) ⇒ x ≡ x ′ ( m o d P i gcd ( P i , C i ) ) \begin{aligned}&C_ix\equiv A_i(\mod P_i)\\&\text{显然有\ }C_ix+P_iy=A_i\\&\text{exgcd解得 x' y'}\\&x =x'+k\frac{b}{d}\\&\ \ =x'+k\frac{P_i}{\gcd(P_i,C_i)}\\& (\mod{\frac{P_i}{\gcd(P_i,C_i)}})\\&\Rightarrow x\equiv x'(\mod\frac{P_i}{\gcd(P_i,C_i)}) \end{aligned} Cix≡Ai(modPi)显然有 Cix+Piy=Aiexgcd解得 x’ y’x=x′+kdb =x′+kgcd(Pi,Ci)Pi(modgcd(Pi,Ci)Pi)⇒x≡x′(modgcd(Pi,Ci)Pi)
拓展中国剩余定理求解即可。
注意特判 A i > P i A_i>P_i Ai>Pi 的情况即可,此时 P i = 1 P_i=1 Pi=1,答案显然就是 m a x { A [ i ] u s e [ i ] } max\{\cfrac{A[i]}{use[i]}\} max{ use[i]A[i]}
Code
#include
using namespace std;
typedef long long ll;
#define int long long
typedef pair<int, int> PII;
const ll N = 5e6 + 7, INF = 0x3f3f3f3f;
const double PI = acos(-1.0), eps = 1e-8;
int n, m, k;
int A[N], P[N], award[N], atk[N], one;
multiset <int> st;
int C[N], use[N];
int ai[N], bi[N];
void init()
{
one = true;
st.clear();
memset(use, 0, sizeof use);
}
int mul(int x, int y, int p)
{
int z = (long double)x / p * y;
int res = (unsigned long long)x * y - (unsigned long long) z * p;
return (res + p) % p;
}
int exgcd(int a, int b, int &x, int &y)
{
if(b == 0) {
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, x, y);
int z = x;
x = y, y = z - (a / b) * y;
return d;
}
int excrt()
{
int x, y, k;
int M = bi[1], ans = ai[1];
for(int i = 2; i <= n; ++ i) {
int a = M, b = bi[i], c = (ai[i] - ans % b + b) % b;
int d = exgcd(a, b, x, y);
int bg = b / d;
if(c % d != 0) return -1;
x = mul(x, c / d, bg);
ans += x * M;
M *= bg;
ans = (ans % M + M) % M;
}
return (ans % M + M) % M;
}
void sp_work()
{
int ans = - INF;
for(int i = 1; i <= n; ++ i) {
if(use[i]) {
ans = max(ans, (int)ceil((double)A[i] / use[i]));
}
}
printf("%lld\n", ans);
}
bool work()
{
int x, y;
for(int i = 1; i <= n; ++ i) {
int a = use[i], b = P[i], c = A[i];
int d = exgcd(a, b, x, y), bg = b / d;
if(c % d != 0)
return false;
x = mul(x, c / d, bg);
x = (x % b + b) % b;
ai[i] = x;
bi[i] = bg;
}
return true;
}
void solve()
{
init();
scanf("%lld%lld", &n, &m);
for(int i = 1; i <= n; ++ i) scanf("%lld", &A[i]);
for(int i = 1; i <= n; ++ i) {
scanf("%lld", &P[i]);
if(A[i] > P[i])
one = false;
}
for(int i = 1; i <= n; ++ i) scanf("%lld", &award[i]);
for(int i = 1; i <= m; ++ i) {
scanf("%lld", &atk[i]);
st.insert(atk[i]);
}
for(int i = 1; i <= n; ++ i) {
multiset <int> :: iterator it;
if(A[i] < *st.begin()) it = st.begin();
else it = -- st.upper_bound(A[i]);
use[i] = *it;
st.insert(award[i]);
st.erase(it);
}
if(one == false) {
sp_work();
return ;
}
bool win = work();
if(win == 0) {
puts("-1");
return ;
}
printf("%lld\n", excrt());
return ;
}
signed main()
{
int t;
scanf("%lld", &t);
while(t -- ) {
solve();
}
return 0;
}
/*
487472861809
3871865111
7560798679
584853762636
670310334583
*/
Weblink
https://codeforces.com/contest/1501/problem/D
Solution 1 二分 + 中国剩余定理
Solution 2 二分 + 拓展欧几里德
Weblink
https://codeforc.es/gym/102055/problem/K
Translation
给出 n , c n , c n,c , n = p ∗ q n = p ∗ q n=p∗q , p p p 和 q q q 是 x x x 附近相邻的两个质数, c = f 2 30 + 3 m o d n c = f^{2^{30}+3} \ mod\ n c=f230+3 mod n。求出 f f f 的值。
Solution 1 中国剩余定理
Solution 2 拓展欧几里德
Weblink
https://nanti.jisuanke.com/t/41383
Solution
斐波那契博弈 + 拓展中国剩余定理
…就是个板子题,只不过凉心数据会爆 long long
…题目里也不给数据范围就离谱, n ≤ 1 0 15 n\le 10^{15} n≤1015
#include
using namespace std;
const int N = 5000007;
#define ll long long
#define LL __int128
LL n;
ll ai[N], bi[N];
LL f[N];
LL exgcd(LL a, LL b, LL &x, LL &y)
{
if(b == 0) {
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, x, y);
LL z = x;
x = y; y = z - a / b * y;
return d;
}
LL mul(LL a, LL b, LL c)
{
LL res = 0;
while(b) {
if(b & 1) res = (res + a) % c;
a = (a + a) % c;
b >>= 1;
}
return res;
}
LL EXCRT(int n)
{
LL x, y, k;
LL M = bi[1];
LL ans = ai[1];
for(int i = 2; i <= n; ++ i) {
LL a = M, b = bi[i], c = (ai[i] - ans % b + b) % b;
LL d = exgcd(a, b, x, y);
LL bg = b / d;
if(c % d != 0) return -1;
x = mul(x, c / d, bg);
ans += x * M;
M *= bg;
ans = (ans % M + M) % M;
}
return (ans % M + M) % M;
}
//n = b (mod a)
//b -> ai(常数), a -> bi(模数)
ll k;
int main()
{
scanf("%lld", &k);
for(ll i = 1; i <= k; ++ i) {
scanf("%lld%lld", &bi[i], &ai[i]);
}
n = EXCRT(k);
if(n == -1) {
puts("Tankernb!");
return 0;
}
f[1] = f[2] = 1;
for(int i = 3; i <= 75; ++ i) {
f[i] = f[i - 1] + f[i - 2];
}
for(int i = 1; i <= 75; ++ i) {
if(f[i] == n) {
puts("Lbnb!");
return 0;
}
}
puts("Zgxnb!");
return 0;
}