- 声明 : 本人刚学算法一年,都是自己做题常用的模板,不时总结下。
大致分为:乱七八糟, 数论,图论,动态规划,几何,Java
还有一些奇葩定理,
常用素数:
1
e
8
左
右
:
91815541
,
68861641
1e8左右:91815541,68861641
1e8左右:91815541,68861641
1
e
9
左
右
:
1
e
9
+
9
,
917120411
,
687840301
,
1222827239
1e9左右:1e9 + 9,917120411,687840301,1222827239
1e9左右:1e9+9,917120411,687840301,1222827239
奇葩定理:
【1】高效求出n的约数的个数:
对 数 X 做质因数分解, 结果假定是 1^1 * a ^ A * b ^ B .... z ^ Z , 那 X 的因数个数应该是 (A+1) * (B+1) * .... (Z+1) - 1
而 X^2 的因数个数应该是 (2*A + 1) * (2*B + 1) * ... (2*Z + 1) -1 ....
比如 12 可以写做事 1 * 2^2 * 3, 所以 12 的因数数量是 (2+1) * (1+1) - 1 = 5 个, 分别是 1 2 3 4 6
而 12^2 = 144 有 (2*2+1) * (2*1+1) - 1 = 14 个, 分别是 1 2 3 4 6 8 9 12 16 18 24 36 48 72
【1.1】高效求出n的所有约数和:
n
=
a
1
k
1
a
2
k
2
.
.
.
a
n
k
n
n = a_1^{k_1}a_2^{k_2}...~ a_n^{k_n}
n=a1k1a2k2... ankn
所
有
约
数
和
S
=
(
1
+
a
1
+
a
1
2
+
.
.
.
+
a
1
k
1
)
∗
(
1
+
a
2
+
a
2
2
+
.
.
.
+
a
2
k
2
)
∗
.
.
.
∗
(
1
+
a
n
+
a
n
2
+
.
.
.
+
a
n
k
n
)
所有约数和S = (1 + a_1 + a_1^2 + ... +a_1^{k_1})* (1 + a_2 + a_2^2 + ... +a_2^{k_2})*...* (1 + a_n + a_n^2 + ... +a_n^{k_n})
所有约数和S=(1+a1+a12+...+a1k1)∗(1+a2+a22+...+a2k2)∗...∗(1+an+an2+...+ankn)
【2】快速判断 C n m 的 奇 偶 性 C_n^m的奇偶性 Cnm的奇偶性 : 如果 (n&m) == m C n m C_n^m Cnm为奇数
【3】c++的字符串用print输出:printf("%s\n", st.c_str());
【4】迭代器:::iterator
乱七八糟模板
- 头文件,宏定义,读入挂
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <string>
#include <bitset>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <algorithm>
#include <sstream>
#include <stack>
#include <iomanip>
#define ll long long
#define ull unsigned long long
#define PI acos(-1.0)
#define eps 1e-12
#define fi first
#define se second
#define MEM(a,b) memset((a),(b),sizeof(a))
#define mod(x) ((x)%MOD)
#define pii pair<int,int>
#define wz cout<<"-----"<<endl;
const int INF_INT = 2147483647;
const ll INF_LL = 9223372036854775807LL;
const ull INF_ULL = 18446744073709551615Ull;
const ll P = 92540646808111039LL;
const ll maxn = 1e5 + 10, MOD = 1e9 + 7;
const int Move[4][2] = {-1,0,1,0,0,1,0,-1};
const int Move_[8][2] = {-1,-1,-1,0,-1,1,0,-1,0,1,1,-1,1,0,1,1};
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
- 结构体重载运算符
//与平时cmp函数 相反
bool operator < (const node &p) const {
return r > p.r;
}
博弈
- 威佐夫博弈
有两堆各若干个物品,两个人轮流从任意一堆中取出至少一个或者同时从两堆中取出同样多的物品,规定每次至少取一个,至多不限,最后取光者胜利。
必败态: (0,0),(1,2),(3,5),(4 ,7),(6,10),(8,13)
黄金比例 t = 5 + 1 2 \frac{\sqrt5 + 1}{2} 25+1
假设差值为 k,必败态为: 【int(k * t),int(k*t) + k】
数论模板
- 乱七八糟的公式
-
勾股数
注意特判n = 1、2的时候
当 n 为 奇 数 , 且 大 于 1 , 另 外 两 个 勾 股 数 为 n 2 2 , n 2 2 + 1 n为奇数,且大于1,另外两个勾股数为~~ \frac{n^2}{2} ,~~\frac{n^2}{2} + 1 n为奇数,且大于1,另外两个勾股数为 2n2, 2n2+1
当 n 为 偶 数 , 且 大 于 2 , 另 外 两 个 勾 股 数 为 n 2 4 − 1 , n 2 4 + 1 n为偶数,且大于2,另外两个勾股数为 ~~\frac{n^2}{4}-1 ,~~\frac{n^2}{4} + 1 n为偶数,且大于2,另外两个勾股数为 4n2−1, 4n2+1 -
降幂公式
a b % c = a b % φ ( c ) + φ ( c ) % c a~^b~\%~c~=~a~^{b\%φ(c)~+~φ(c)}~\%~c a b % c = a b%φ(c) + φ(c) % c
- 线性求逆元
在模质数p下,求1-n模p下的逆元,可以递推出来。
i n v [ i ] = ( p − p / i ) ∗ i n v [ p % i ] % p inv[i] ~=~ (p - p ~/~ i)~ *~ inv~[p ~\%~ i ]~ \% ~p inv[i] = (p−p / i) ∗ inv [p % i] % p
推导如下:
设 t = p / i , k = p % i → t ∗ i + k ≡ 0 ( m o d p ) → − t ∗ i ≡ k ( m o d p ) 设t = p ~/~ i,~~~~ k = p ~\%~ i ~~~~~~ → ~~~~~~ t ~*~ i + k ~≡~ 0(~mod~ p)~~~~~~ → ~~~~~~-t~*~i~≡~k(mod~p) 设t=p / i, k=p % i → t ∗ i+k ≡ 0( mod p) → −t ∗ i ≡ k(mod p)
然后两边同时除以 i ∗ k 得 , − t ∗ i n v [ k ] ≡ i n v [ i ] ( m o d p ) i ~ * ~k得, - t~ *~ inv[~k~] ~≡~ inv[~i~](mod ~p) i ∗ k得,−t ∗ inv[ k ] ≡ inv[ i ](mod p)
再把 t 和 k 替 换 掉 可 以 得 到 : t和k替换掉可以得到: t和k替换掉可以得到: i n v [ i ] = ( p − p / i ) ∗ i n v [ p % i ] % p inv[i] ~=~ (p - p ~/~ i)~ *~ inv~[p ~\%~ i ]~ \% ~p inv[i] = (p−p / i) ∗ inv [p % i] % p
int inv[manx];
inv[1] = 1;
for (int i = 2; i < maxn; i++) {
inv[i] = (p - p / i) * 1ll * inv[p % i] % p;
}
- 快速幂
ll quick_pow(ll a,ll b) {
ll res = 1;
while (b) {
if (b & 1) {
res = a * res;
}
a = a * a;
b >>= 1;
}
return res;
}
- 矩阵快速幂
#include<bits/stdc++.h>
#define ll long long
#define mod(x) ((x)%MOD)
using namespace std;
const ll MOD = 1e9 + 7;
struct mat{
ll m[3][3];
}a,ans,unit;
void init() {
memset(unit.m,0,sizeof(unit.m));
memset(a.m,0,sizeof(a.m));
unit.m[0][0] = 1;
unit.m[1][1] = 1;
a.m[0][0] = 3;
a.m[0][1] = 1;
a.m[1][0] = 1;
a.m[1][1] = 3;
}
mat operator * (mat m1,mat m2) {
mat t;
ll r;
for(int i = 0;i < 3;i++) {
for(int j = 0;j < 3;j++) {
r = 0;
for(int k = 0;k < 3;k++) {
r = mod(r*1ll + mod(mod(m1.m[i][k])*1ll*mod(m2.m[k][j])));
}
t.m[i][j] = r;
}
}
return t;
}
mat quick_pow(ll x) {
mat t = unit;
while(x) {
if(x & 1) {
t = t*a;
}
a = a*a;
x >>= 1;
}
return t;
}
int main(){
init();
ans = quick_pow(n);
}
- 素数的两种筛法
bool isp[maxn];
int p[maxn], len;
bool isp[100];
void init() {
int m = (int)sqrt(maxn+0.5);
for(int i = 2;i <= m;i++) {
if(!isp[i]) {
for(int j = i*i;j <= maxn;j += i) {
isp[j] = true;
}
}
}
}
void init() { //推荐这个,较快
isp[0] = isp[1] = true;
for (int i = 2; i < maxn; i++) {
if(!isp[i]) p[++len] = i;
for (int j = 1; j <= len && p[j]*i < maxn; j++) {
isp[i*p[j]] = true;
if (i%p[j] == 0) break;
}
}
}
- 欧拉函数
在数论,对正整数n,欧拉函数是小于n的正整数中与n互质的数的数目(φ(1)=1)。
int euler_phi(int n){ //单个值
int m = (int)sqrt(n + 0.5);
int ans = n;
for (int i = 2;i <= m;i++){
if (n%i == 0){ //如果存在素因子
ans = ans/i*(i-1);
while (n%i == 0) n/=i;
}
}
if(n > 1) ans = ans/n*(n-1); //考虑n本身
return ans;
}
void phi_table(int n,int *phi){ //欧拉表
for (int i = 1;i <= n;i++) phi[i] = i;
for(int i = 2;i <= n;i++){
if(phi[i] == i){ //类似于Eratosthenes筛法这里
for(int j = i;j <= n;j+=i){
phi[j] = phi[j]/i*(i-1);
}
}
}
}
- 欧几里得GCD,扩展~
欧几里德算法又称辗转相除法,用于计算两个整数a,b的最大公约数(greatest common divisor)。 扩展欧几里德算法是用来在已知a, b求解一组x,y,使它们满足贝祖等式: ax+by = gcd(a, b) =d(解一定存在,根据数论中的相关定理)。扩展欧几里德常用在求解模线性方程及方程组中。
ll gcd(ll a,ll b) {
return b == 0 ? a : gcd(b, a % b);
}
void egcd(ll a, ll b, ll &d, ll &x, ll &y) {
if(!b) d=a,x=1,y=0;
else egcd(b, a % b, d, y, x),y -= x * (a / b);
}
- 逆元
方程ax≡1(mod p),的解称为a关于模p的逆,当gcd(a,p)==1(即a,p互质)时,方程有唯一解,否则无解。 对于一些题目会要求把结果MOD一个数,通常是一个较大的质数,对于加减乘法通过同余定理可以直接拆开计算,但对于(a/b)%MOD这个式子,是不可以写成(a%MOD/b%MOD)%MOD的,但是可以写为(a*b^-1)%MOD,其中b^-1表示b的逆元。
ll getinv (ll a,ll p) {
ll d, x, y;
egcd (a, p, d, x, y);
return (x + p) % p == 0 ? p : (x + p) % p;
}
- 中国剩余定理(CRT) ,扩展~
中国剩余定理给出了以下的一元线性同余方程组:
假设整数m1,m2, ... ,mn两两互质,则对任意的整数:a1,a2, ... ,an,方程组 有解,即x,扩展剩余定理就是m1,m2···mn,这几个数不两两互质的情况
// M 为 lcm(m1,m2...,mn)
ll CRT(ll M){
ll sum=0,tmp,v;
for (int i=1;i<=cnt;i++){
tmp=M/m[i];
v=getInv(tmp,m[i]);
sum=(sum+tmp*a[i]*v)%M;
}
return sum;
}
/*以下是ECRT*/
///这里的是从下标是从1开始, r数组代表 余数,m 数组代表除数
bool merge(ll &a1,ll &m1,ll a2,ll m2){
ll c,d,x,a3,m3;
c=a2-a1;d=__gcd(m1,m2);
if (c%d!=0) return false;
c=c/d;m1=m1/d;m2=m2/d;
x=getinv(m1,m2);
x=(x*c)%m2;
x=x*(m1*d)+a1;
m3=m1*m2*d;
a3=(x%m3+m3)%m3;
a1=a3;m1=m3;
return true;
}
ll ECRT(){
ll A=r[1],M=m[1];
for (int i=2;i<=n;i++) //无解返回 -1
if (!merge(A,M,r[i],m[i]))
return -1;
return (A%M+M)%M;
}
- 错排公式
问题: 十本不同的书放在书架上。现重新摆放,使每本书都不在原来放的位置。有几种摆法? 这个问题推广一下,就是错排问题,是组合数学中的问题之一。考虑一个有n个元素的排列,若一个排列中所有的元素都不在自己原来的位置上,那么这样的排列就称为原排列的一个错排。 n个元素的错排数记为D(n)。 研究一个排列错排个数的问题,叫做错排问题或称为更列问题。
//dp[i] = (i - 1)*(dp[i - 1] + dp[i - 2]); i > 2
ll a = 0,b = 1,c;
for (int i = 3; i <= n; i++) {
c = ((i - 1) * 1ll * (a + b)) % MOD;
a = b;
b = c;
}
printf("%lld\n",c);
- O(n) 求组合数
C n k = n − k + 1 k C n k − 1 C_n^k = \frac{n - k + 1}{k} C_n^{k - 1} Cnk=kn−k+1Cnk−1
从开始从左到右递推,注意爆int
C[0] = 1;
for(int i = 1; i <= n; i++)
C[i] = C[i - 1] * (n - i + 1) / i;
-
卡特兰数列
h(n)=C(2n,n)/(n+1) (n=0,1,2,...)
h(n)=c(2n,n)-c(2n,n-1)(n=0,1,2,...)
C(m+n,n)−C(m+n,n−1)
-
阶乘逆元
fac[0] = 1;
for (int i = 1; i <= maxn; i++)
fac[i] = mod(fac[i - 1] * i);
rfac[maxn] = qpow(fac[maxn],MOD - 2);
for (int i = maxn;i > 0; i--)
rfac[i - 1] = mod(rfac[i] * i);
- Lucas
求 C n m % p C_n^m\%p Cnm%p,其中p为质数
$n=n_k∗pk+n_{k−1}∗p{k−1}+…+n_2∗p^2+n_1∗p+n_0 $
m
=
m
k
∗
p
k
+
m
k
−
1
∗
p
k
−
1
+
.
.
.
+
m
2
∗
p
2
+
m
1
∗
p
+
m
0
m
m=m_k∗p^k+m_{k−1}∗p^{k−1}+...+m_2∗p^2+m_1∗p+m_0m
m=mk∗pk+mk−1∗pk−1+...+m2∗p2+m1∗p+m0m
则 C n m = ∏ i = 0 k C n i m i 则C_n^m=∏_{i=0}^kC_{ni}^{mi} 则Cnm=∏i=0kCnimi
ll C(ll nn, ll mm) {
ll up = 1, down = 1;
for (int i = nn - mm + 1; i <= nn; i++) up = mod(up * 1ll *i);
for (int i = 1; i <= mm; i++) down = mod(down * 1ll *i);
return mod(up * qpow(down, MOD - 2));
}
ll lucas(ll n, ll m) {
if(m == 0) return 1;
return mod(lucas(n / MOD, m / MOD) * 1ll * C(n % MOD,m % MOD));
}
- 斯特林公式,计算阶乘
公式如下:
## N ! = 2 π n ( n e ) n N! = \sqrt{{2}\pi n }(\frac ne)^n N!=2πn(en)n
化简如下:
log 10 ( n ! ) = log 10 ( 2 π n ( n e ) n ) \log_{10}(n!) = \log_{10}(\sqrt{{2}\pi n }(\frac ne)^n) log10(n!)=log10(2πn(en)n)
原式 = ln 2 π n ( n e ) n ln 10 \frac {\ln{\sqrt{{2}\pi n }(\frac ne)^n}}{\ln10} ln10ln2πn(en)n
原式 = 0.5 ∗ ln ( 2 π n ) + n ∗ ln n − n ln 10 \frac {0.5*\ln{{(2\pi n) } + n*\ln n - n}}{\ln10} ln100.5∗ln(2πn)+n∗lnn−n
###参考代码
#include<bits/stdc++.h>
#define PI acos(-1.0)
using namespace std;
int main(){
int T;cin>>T;
while(T--) {
int x;cin>>x;
int res = (int)((0.5*log(2*PI*x) + x*log(x) - x)/log(10));
cout<<res+1<<endl;
}
return 0;
}
- 原根:
原根 :待补
还有
还有就是: 如果p有原根,则它恰有φ(φ§)个不同的原根(无论p是否为素数都适用)
参考这里
//这是求最小原根的代码
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
bool isp[maxn];
int p[maxn],len;
void init() {
isp[0] = isp[1] = 1;
for (int i = 2; i < maxn; i++) {
if (!isp[i]) {
p[len++] = i;
for (int j = i + i; j < maxn; j += i) {
isp[j] = true;
}
}
}
}
ll s[maxn];
ll qpow(ll a, ll b, ll p) {
ll res = 1;
while (b) {
if (b & 1) res = (res * a) % p;
a = (a * a) % p;
b >>= 1;
}
return res;
}
ll phi(ll x) {
ll res = x;
ll m = (ll) sqrt(x + 0.5);
for (int i = 2; i <= m; i++) {
if (x % i == 0) {
res = res / i * (i - 1);
while (x % i == 0) x /= i;
}
}
if (x != 1) res = res / x * (x - 1);
return res;
}
int main() {
init();
ll n;
while (~scanf("%lld", &n)) {
ll t = phi(n);
cout<<t<<endl;
int r = 0;
memset(s, 0, sizeof s);
for (int i = 0; i < len; i++) {
if (t % p[i] == 0) {
s[r++] = p[i];
while (t % p[i] == 0) t /= p[i];
}
}
if (t > 1) s[r++] = t;
vector<int> res;
for (int i = 2; i < n; i++) {
bool flg = false;
for (int j = 0; j < r; j++) {
ll t = (n - 1) / s[j];
if (qpow(i, t, n) == 1) {
flg = true;break;
}
}
if (!flg) {
res.push_back(i);
}
}
if (res.size() == 0)puts("-1");
else for (int i = 0; i < res.size(); i++) printf("%d%c", res[i], i == res.size() - 1 ? '\n' : ' ');
}
}
- 伯努利数:
主要解决等幂和的问题,Faulhaber
公式如下:
∑ i = 1 n i k = 1 k + 1 ∑ i = 0 k C k + 1 i B i n k + 1 − i \sum_{i=1}^n i^k = \frac{1}{k+1} \sum_{i=0}^{k}C_{k+1}^{i}B_{i}n^{k+1-i} ∑i=1nik=k+11∑i=0kCk+1iBink+1−i
伯努利数满足: ∑ k = 0 n C n + 1 k B k = 0 \sum_{k=0}^{n}C_{n+1}^k B_k = 0 ∑k=0nCn+1kBk=0
伯努利数递归定义如下:
B
0
=
1
B_0 = 1
B0=1
B
n
=
−
1
n
+
1
(
C
n
+
1
0
B
0
+
C
n
+
1
1
B
1
+
⋅
⋅
⋅
+
C
n
+
1
n
−
1
B
n
−
1
)
B_n = -\frac{1}{n+1}(C_{n+1}^0B_0+C_{n+1}^1B_1+···+C_{n+1}^{n-1}B_n-1)
Bn=−n+11(Cn+10B0+Cn+11B1+⋅⋅⋅+Cn+1n−1Bn−1)
参考:https://www.bilibili.com/video/av62113459?p=1
https://zh.wikipedia.org/wiki/%E4%BC%AF%E5%8A%AA%E5%88%A9%E6%95%B0
对于等幂和的问题,在已知模数P后,可以先逆元预处理出来组合数,以及伯努利数
const ll maxn = 2000 + 10, MOD = 1e9 + 7;
ll C[maxn][maxn];
ll inv[maxn];
ll B[maxn];
void init() {
for (int i = 1; i < maxn; i++) {
for (int j = 1; j < i; j++) {
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % MOD;
}
C[i][0] = C[i][i] = 1;
}
inv[1] = 1;
for (int i = 2; i < maxn; i++) {
inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
}
B[0] = 1;
for (int i = 1; i < maxn; i++) {
B[i] = 0;
for (int j = 0; j < i; j++) {
B[i] = (B[i] + C[i + 1][j] * B[j] % MOD) % MOD;
}
B[i] = (B[i] * (-inv[i + 1]) % MOD + MOD) % MOD;
}
}
ll getPkn(ll n, ll k) {
n++;
n %= MOD;
ll res = 0, tmp = n;
for (int i = k; i >= 0; i--) {
res = (res + (n * B[i] % MOD )* C[k + 1][i]) % MOD;
n = (n * tmp) % MOD;
}
res = (res * inv[k + 1]) % MOD;
return res;
}
图论模板
- 并查集
int findset(int x) {
return pa[x] != x ? pa[x] = findset(pa[x]) : x;
}
void unite (int x,int y) {
x = findset(x);
y = findset(y);
if(x == y) return ;
res--;
if(ran[x] < ran[y]) {
pa[x] = y;
} else {
pa[y] = x;
if(ran[x] == ran[y]) ran[x]++;
}
}
- 单源最短路spfa
const int maxn = 1e5 + 10;
vector<pair<int,int> > E[maxn];
int inq[maxn],n,m;
ll dis[maxn];
queue<int> q;
void spfa() {
for(int i = 0;i < maxn;i++) {
dis[i] = 2000000000;
inq[i] = 0;
}
dis[1] = 0;
q.push(1);
while(!q.empty()) {
int t = q.front();
q.pop();inq[t] = 0;
for(int i = 0;i < E[t].size();i++) {
int to = E[t][i].first;
ll di = E[t][i].second;
if(dis[to] > dis[t] + di) {
dis[to] = dis[t] + di;
if(!inq[to]) {
inq[to] = 1;
q.push(to);
}
}
}
}
}
//SLF优化:Small Label First 策略,设要加入的节点是j,队首元素为i,若dist(j) < dist(i),则将j插入队首,否则插入队尾
void spfa() {
for (int i = 0; i < maxn; i++) dis[i] = INF;
deque<int> q;
dis[s] = 0;
q.push_back(s);
while (!q.empty()) {
int from = q.front();
q.pop_front();
inq[from] = 0;
for (int i = 0; i < E[from].size(); i++) {
int to = E[from][i].first;
int di = E[from][i].second;
if(dis[to] > dis[from] + di) {
dis[to] = dis[from] + di;
if(inq[to] == 0) {
inq[to] = 1;
if(q.size() && dis[to] < dis[from]) {
q.push_front(to);
} else {
q.push_back(to);
}
}
}
}
}
}
- 单源最短路 迪杰斯特拉 dijkstra
队列优化
const int maxn = 1e5 + 10;
vector<pair<int,int> > E[maxn];
int n,m,dis[maxn];
priority_queue<pair<int,int> > q;
void dij() {
for(int i = 0;i < maxn;++i) {
dis[i] = 1e9;
}
dis[1] = 0;
q.push(make_pair(0,1));
while (!q.empty()) {
int t = q.top().second;
q.pop();
for(int i = 0;i < E[t].size();++i) {
int to = E[t][i].first;
int di = E[t][i].second;
if(dis[to] > dis[t] + di) {
dis[to] = dis[t] + di;
q.push(make_pair(-dis[to],to));
}
}
}
}
动态规划
- 最长上升子序列
/*求LIS的长度*/
/*n方的写法,易写*/
int ans = 0;
for(int i = 1; i <= n; ++i) {
scanf("%d", &num[i]);
dp[i] = 1;
for(int j = 1; j < i; ++j) {
if(num[j] <= num[i]) {
dp[i] = max(dp[i], dp[j] + 1);
}
}
ans = max(ans, dp[i]);
}
/*手写二分,或者lower_brond*/
int binary(int x,int rr){
int l = 0,r = rr,ans;
while(l <= r){
int mid = l + r >> 1;
if(b[mid] >= x) ans = mid,r = mid - 1;
else l = mid + 1;
}
return ans;
}
int n;
for(int i = 1; i <= n;i++){
cin>>a[i];
}
int len = 1;
b[1] = a[1];
for(int i = 2;i <= n;i++){
if(a[i] > b[len]){
b[++len] = a[i];
}
else {
int t = binary(a[i],len);//也可以用c++自带的lower_pound,自写也可以
b[t] = a[i];
}
}
cout<<len<<endl;
- 背包dp
//01背包
for(int i = 1;i <= n;i++){
for(int j = V;j >= w[i];j--){
dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
}
}
//完全背包
for(int i = 0;i < n;i++){
for(int j = w[i];j <= e-s;j++){
dp[j] = min(dp[j],dp[j-w[i]]+v[i]);
}
}
//多重背包
for(int i = 0;i < n;i++){
for(int k = 0;k < b[i];k++){
for(int j = n;j >= v[i];j--){
dp[j] = max(dp[j],dp[j-v[i]]+w[i]);
}
}
}
//01背包 ,记录路径
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int pre[N],dp[N],v[N],ans[N];
void p(int x){
if(pre[x] == 0){
cout<<ans[x];return;
}
p(pre[x]);
cout<<' '<<ans[x];
}
int main(){
ios_base::sync_with_stdio(0);
int n,m;cin>>n>>m;
for(int i = 0;i < n;i++)cin>>v[i];
for(int i = 0;i < N;i++)pre[i] = -1,dp[i] = -INF;
sort(v,v+n);
dp[0] = 0; //和01背包类似,因为是恰好装满,其他只要赋上负无穷
for(int i = 0;i < n;i++){
for(int j = m;j >= v[i];j--){
if(dp[j] <= dp[j-v[i]]+1){
dp[j] = dp[j-v[i]]+1;
ans[j] = v[i];
pre[j] = j - v[i];
}
}
}
if(dp[m] <= 0) cout<<"No Solution";
else p(m);
cout<<endl;
return 0;
}
数据结构
- 树状数组
/*
单点更新,区间查询,(也可区间更新加,区间操作需要存的是差分数组),逆序对等等
*/
int lowerbit(int x) {
return x & -x;
}
void add(int p, int x) {
while (p < maxn) {
d[p] += x;
p += lowerbit(p);
}
}
int sum(int p) {
int res = 0;
while (p) {
res += d[p];
p -= lowerbit(p);
}
return res;
}
// 二维树状数组
int lowbit(int x) {
return x & -x;
}
void add(int x, int y, int v) {
x++;y++; // 输入的下标是从0开始的
while (x < _max) {
int yy = y;
while (yy < _max) {
c[x][yy] += v;
yy += lowbit(yy);
}
x += lowbit(x);
}
}
int sum(int x, int y) {
x++;y++;
int ret = 0;
while (x > 0) {
int yy = y;
while (yy > 0) {
ret += c[x][yy];
yy -= lowbit(yy);
}
x -= lowbit(x);
}
return ret;
}
// 初始化
NumMatrix(vector<vector<int>>& matrix) {
int n = matrix.size();
if (!n) return ;
int m = matrix[0].size();
memset(c, 0, sizeof c);
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
add(i, j, matrix[i][j]);
}
}
}
// 单点更新,需要先查询
void update(int row, int col, int val) {
add(row, col, val - sumRegion(row, col, row, col));
}
// 和维护二维前缀和类似
int sumRegion(int row1, int col1, int row2, int col2) {
return sum(row2, col2) - sum(row1 - 1, col2) - sum(row2, col1 - 1) + sum(row1 - 1, col1 - 1);
}
- ST表
这里记录下,ST表属于RMQ问题,就是区间最值问题,运用倍增的思路,sq[i] 表示 2 i 2^i 2i,Log[i]数组表示 l o g 2 i log_2{i} log2i下取整, s t [ i ] [ j ] st[i][j] st[i][j]表示区间 [ i , i + 2 j − 1 ] [i,i + 2^j - 1] [i,i+2j−1]的最值
像这样,左右两个区间再次这么划分
以求最大值为例,总区间的最大值=max(左区间最大值,右区间最大值)
即状态转移方程为 f [ x ] [ j ] = m a x ( f [ x ] [ j − 1 ] , f [ x + 2 j − 1 ] [ j − 1 ] f[x][j]=max(f[x][j-1],f[x+2^j-1][j-1] f[x][j]=max(f[x][j−1],f[x+2j−1][j−1]
询问区间 [ l , r ] [l,r] [l,r]的最大值
我们设 x = l o g 2 ( l − r + 1 ) x=log_2{(l-r+1)} x=log2(l−r+1)
那么答案就是 m a x ( f [ l ] [ x ] , f [ r − 2 x + 1 ] [ x ] ) max(f[l][x],f[r-2^x+1][x]) max(f[l][x],f[r−2x+1][x])
int sq[31], Log[maxn], st[maxn][31],a[maxn];
void init() {
sq[0] = 1;
for (int i = 1; i < 31; i++) sq[i] = sq[i - 1] << 1;
Log[0] = -1;
for (int i = 1; i < maxn; i++) Log[i] = Log[i >> 1] + 1;
}
int main() {
init();
int n,m; cin >> n >> m;
for (int i = 1; i <= n; i++) scanf("%d", &st[i][0]);
for (int i = 1; i < 31; i++)
for (int j = 1; j <= n; j++) {
if (j + sq[i] - 1 > n) break;
st[j][i] = max(st[j][i - 1], st[j + sq[i - 1]][i - 1]);
}
while (m--) {
int l, r; scanf("%d%d", &l, &r);
int t = Log[r - l + 1];
printf("%d\n", max(st[l][t], st[r - sq[t] + 1][t]));
}
return 0;
}
几何
Java
- 大数基本操作
import java.util.Scanner;
import java.math.BigInteger;
public class Main {
public static void main(String[] args) {
BigInteger[] a = new BigInteger[10100];
a[0] = BigInteger.valueOf(1);
for(int i = 1;i < 10100;i++) {
a[i] = a[i-1].multiply(BigInteger.valueOf(i));
}
Scanner in = new Scanner(System.in);
while (in.hasNextInt()) {
int x = in.nextInt();
System.out.println(a[x]);
}
}
}
字符串
- KMP
void getNext(string s) {
int len = s.size();
int k = -1,j = 0;
Next[0] = k;
while (j < len) { // 注意这里,因为这里要匹配多次
if(k == -1 || s[k] == s[j]) {
j++,k++;
Next[j] = k;
} else k = Next[k];
}
}
//下面是优化的版本
void getNext(string s) {
int len = s.size();
int k = -1,j = 0;
Next[0] = k;
while (j < len) { // 注意这里,因为这里要匹配多次
if(k == -1 || s[k] == s[j]) {
j++,k++;
if (s[j] == s[k]) {
Next[j] = Next[k];
} else {
Next[j] = k;
}
} else k = Next[k];
}
}
//匹配如下:
//如果s1是模式串 s2是匹配串 简单就是 s1是短的,s2是长的
int res = 0;
int lenS = s2.size();
int j = 0,i = 0;
while (i < lenS) {
if (j == -1 || s2[i] == s1[j]) {
i++,j++;
if (j >= s1.size()) {//如果只匹配一次,直接break
res++;
j = Next[j]; // 注意
}
} else {
j = Next[j];
}
}
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
char S[maxn];
struct Trie{
int next[26];
int cnt;
void init() {
cnt = 0;
memset(next, -1, sizeof next);
}
}T[maxn];
int le;
void Insert(string s) {
int p = 0;
for (int i = 0; i < s.size(); i++) {
int r = s[i] - 'a';
if(T[p].next[r] == -1) {
T[le].init();
T[p].next[r] = le++;
}
p = T[p].next[r];
T[p].cnt++;
}
}
void query(string s) {
int p = 0;
for (int i = 0; i < s.size(); i++) {
int r = s[i] - 'a';
if(T[p].next[r] == -1) {
cout<<0<<endl;
return ;
}
p = T[p].next[r];
}
cout<<T[p].cnt<<endl;
}
int main() {
ios_base::sync_with_stdio(0);
int n;cin>>n;
T[0].init();
le = 1;
for (int i = 0; i < n; i++) {
string s;cin>>s;
Insert(s);
}
int m;cin>>m;
while (m--) {
string s;cin>>s;
query(s);
}
return 0;
}