文章目录
0、一般算法
二维前缀和
#include<bits/stdc++.h>
using namespace std;
const int num = 6;
int mapp[num][num] , sum[num][num];
int main(){
int x1,y1,x2,y2;
for(int i = 1; i < num; i ++)
for(int j = 1; j < num; j ++)
mapp[i][j] = 1 + rand() % 5;
for(int i = 1; i < num; i ++)
for(int j = 1; j < num; j ++)
sum[i][j] = mapp[i][j] + sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
while(cin >> x1 >> y1 >> x2 >> y2)
{
cout << sum[x2][y2] - sum[x1][y1] << endl;
}
}
1、 图论
最小生成树
Kruskal算法并查集实现
//板子适配题目洛谷:R18683676
#include<bits/stdc++.h>
using namespace std;
const int MN = 5000 + 5;
const int ME = 200000 + 5;
struct node
{
int u , v , w;
bool operator < (const node &b)const{
return w < b.w;
}
};
vector<node> eage;
int pa[MN] = {0};
int find(int x){
return (pa[x] == x ? x : find(pa[x]));
}
int main(){
int n , e;
cin >> n >> e;
for(int i = 0; i < e; i ++)
{
int u , v , w;
cin >> u >> v >> w;
eage.push_back((node){u,v,w});
}
sort(eage.begin() , eage.end());
for(int i = 0; i < MN; i ++)
{
pa[i] = i;
}
int cnt = 0 , ans = 0;
for(int i = 0; i < e; i ++){
int u = eage[i].u;
int v = eage[i].v;
int w = eage[i].w;
u = find(u);
v = find(v);
if(u == v) continue;
pa[u] = v;
ans += w;
cnt ++;
if(cnt >= n - 1) break;
}
cout << ans;
}
单源最短路径
使用Dijkstra算法和优先队列优化
#include<bits/stdc++.h>
using namespace std;
const int MN = 100000 + 5;
const int ME = 200000 + 5;
const int INF = 2147483647;
struct node{
int v , w;
bool operator < (const node &b)const{
return w > b.w;
}
};
vector<node> eage[MN];
priority_queue<node> q;
int book[MN];
int dis[MN];
int main(){
int n , e , s;
cin >> n >> e >> s;
for(int i = 0; i < e; i ++){
int u , v , w;
cin >> u >> v >>w;
eage[u].push_back((node){v,w});
}
//存入所有边
for(int i = 1; i <= n; i ++) dis[i] = INF;
dis[s] = 0;
q.push((node){s,0});
while(!q.empty()){
node cur = q.top();q.pop();
int u = cur.v;
if(book[u]) continue;
book[u] = 1;
for(int i = 0; i < eage[u].size(); i ++){
int v = eage[u][i].v;
int cost = eage[u][i].w;
if(!book[v] && dis[v] > dis[u] + cost){
//book[]数组标记了在一次更新后,某个点到原点的最短路径
dis[v] = dis[u] + cost;
q.push((node){v,dis[v]});
}
}
}
for(int i = 1; i <= n; i ++)
{
cout << dis[i] << " ";
}
}
简单粗暴的Bellman Ford算法
#include<bits/stdc++.h>
using namespace std;
const long long MN = 10005;
const long long MM = 500005;
struct node
{
long long u , v , w;
};
vector<node> eage;
long long dis[MN];
int Bellman_ford(){
long long n , m , s;
int flag = 0;
cin >> n >> m >> s;
for(long long i = 0; i < m; i ++){
long long u , v , w;
cin >> u >> v >> w;
eage.push_back((node){u,v,w});
}
for(int i = 0; i < MN; i ++){
dis[i] = 2147483647;
}
dis[s] = 0;
for(long long times = 1; times <= n - 1; times ++)
{
for(long long i = 0; i < eage.size(); i ++)
{
long long u = eage[i].u;
long long v = eage[i].v;
long long w = eage[i].w;
if(dis[v] > dis[u] + w)
{
dis[v] = dis[u] + w;
flag = 1;
}
}
}
if(flag == 0) return 0;
for(long long i = 0; i < eage.size(); i ++)
{
long long u = eage[i].u;
long long v = eage[i].v;
long long w = eage[i].w;
if(dis[v] > dis[u] + w)
{
return 1;
}
}
return 0;
}
int main(){
int ans = Bellman_ford();
cout << "Have negative circle ? ";
cout << ans;
}
/*
简单样例1:
5 7 1
1 2 100
1 3 30
1 5 10
3 2 60
3 4 60
4 2 10
5 4 50
没有负环
简单样例2:
4 6 1
1 2 20
1 3 5
4 1 -200
2 4 4
4 2 4
3 4 2
有负环
*/
BF的队列优化SPFA
#include<bits/stdc++.h>
using namespace std;
const int MN = 100000 + 5;
const int NE = 200000 + 5;
struct node
{
int v , w;
};
int book[MN];//判断节点是否在队列当中
queue<int> q;//进队进行松弛操作
int dis[MN];
vector<node> eage[MN];
int cnt[MN]; // 统计每个节点进队次数.如果某点大于节点数,则有负权环
int main(){
int n , e , s = 1;
cin >> n >> e;
for(int i = 0; i < e; i ++)
{
int u , v , w;
cin >> u >> v >> w;
eage[u].push_back((node){v,w});
}
memset(book , 0 , sizeof(book));
memset(dis , 0x3f , sizeof(dis));
while(!q.empty()) q.pop();
dis[s] = 0;
q.push(s);
cnt[s] ++;
while(!q.empty()){
int u = q.front();q.pop();
book[u] = 0;
for(int i = 0; i < eage[u].size(); i ++){
int v = eage[u][i].v;
int w = eage[u][i].w;
if(dis[v] > dis[u] + w)
{
dis[v] = dis[u] + w;
if(!book[v]){
book[v] = 1;
q.push(v);
cnt[v] ++;
if(cnt[v] > n) return 0;
}
}
}
}
for(int i = 1; i <= n; i ++){
cout << dis[i] << " ";
}
}
并查集
#include<bits/stdc++.h>
using namespace std;
const int MN = 10005;
const int MM = 200005;
int pa[MN];
int find(int x){
if(pa[x] == x) return x;
else
return pa[x] = find(pa[x]);
}
int main(){
int n , m;
cin >> n >> m;
for(int i = 0; i < MN; i ++){
pa[i] = i;
}
while(m --){
int chose , u , v;
cin >> chose >> u >> v;
if(chose == 1){
u = find(u);
v = find(v);
pa[u] = v;
}
else{
u = find(u);
v = find(v);
if(u == v)
cout << "Y" << endl;
else
cout << "N" << endl;
}
}
}
2、数论
大数运算
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Scanner;
class MyApplication{
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
BigDecimal b1 = scan.nextBigDecimal();
BigDecimal b2 = scan.nextBigDecimal();
System.out.println(b1.add(b2)); // 加
System.out.println(b1.subtract(b2));// 减
System.out.println(b1.multiply(b2));// 乘
System.out.println(b1.divide(b2)); // 除
BigInteger a = scan.nextBigInteger();
BigInteger b = scan.nextBigInteger();
System.out.println(a.gcd(b)); // gcd
System.out.println(a.remainder(b)); // 取模
scan.close();
}
}
素数筛
#include<bits/stdc++.h>
using namespace std;
const int M = 100005;
int a[M];
int main(){
for(int i = 2; i < M / 2; i ++){
if(a[i]) continue;
for(int j = 2; i * j < M; j ++){
if(i * j < M) a[i * j] = 1;
}
}
}
快速幂
例如: a11=a(20+21+23)
long long ksm(long long a , long long b , long long mod){
long long ans = 1 , base = a;
while(b)
{
if(b&1)
ans = ans * base % mod;
base = base * base % mod;
//不推荐使用复合运算符
b >>= 1;
}
return ans;
}
求逆元(费马小定理)
题外话:
同余方程:
a
≡
b
(
m
o
d
 
m
)
a\equiv b (mod\,m)
a≡b(modm)
a,b对于模m同余
关于取余的一些性质:
1. (a + b) % c = (a % c + b % c) % c
2. (a - b) % c = (a % c - b % c + c) % c
3. (ab) % c = (a % c)(b % c) % c
值得注意的是在除运算中 :
a
b
%
c
=
a
%
c
b
%
c
%
c
\frac{a}{b} \%c = \frac{a\%c}{b\%c}\% c
ba%c=b%ca%c%c 正确吗?
答案很明显,是不对的.
但是我们能够把b转化成逆元
b
−
1
b^{-1}
b−1 简化计算
即:
b
b
−
1
≡
1
(
m
o
d
c
)
bb^{-1} \equiv 1(mod c)
bb−1≡1(modc).
原式就转化为
a
b
%
c
→
a
b
−
1
%
c
\frac{a}{b} \% c \rightarrow ab^{-1} \% c
ba%c→ab−1%c
这里我们需要使用费马小定理:
b
c
−
1
≡
1
(
m
o
d
 
c
)
b^{c - 1} \equiv 1(mod\,c)
bc−1≡1(modc)(c是素数)
变式为:
b
b
c
−
2
≡
1
(
m
o
d
 
c
)
bb^{c-2} \equiv 1(mod\,c)
bbc−2≡1(modc)
也就是说,
b
c
−
2
b^{c-2}
bc−2就是取余逆元.
int inv1 = ksm(b , mod - 2 , mod);
inv1 为b的逆元
辗转相除法求最大公约数和最小公倍数
typedef long long ll;
//求最大公约数
ll gcd(ll a , ll b)
{
return b ? gcd(b,a % b) : a;
}
//求最小公倍数
ll lcm(ll a , ll b)
{
return a * b / gcd(a,b);
}
贝祖公式
性质:
a
x
+
b
y
=
c
ax+by = c
ax+by=c 此类等式成立的充要条件为c是gcd(a,b)的整数倍
推广:
a
x
+
b
y
+
c
z
.
.
.
.
.
+
n
m
=
f
ax + by + cz ..... + nm = f
ax+by+cz.....+nm=f 则f是gcd(a,b,c…n)的整数倍
公式证明(转载)
模板题
//多个数gcd的方法:
int n , ans = 0;
long long num;
cin >> n;
for(int i = 0; i < n; i ++)
{
cin >> num;
num = abs(num);
if(!num) continue;
ans = gcd(num,ans);
}
cout << ans;
拓展欧几里得算法
看了一位大佬的文章
欧几里得算法/扩展欧几里得算法
应用:
(
1
)
(1)
(1)求二元一次方程的通解
(
2
)
(2)
(2)求乘法逆元
拓展欧几里得大致推导:
欧几里得等式:gcd(a,b) = gcd(b,a%b)
我们使用迭代进行计算,设当前等式为:
- a x 1 + b y 2 = g c d ( a , b ) ax_1 + by_2 = gcd(a,b) ax1+by2=gcd(a,b) -----(1)
则下一层等式为:
- b x 2 + ( a % b ) y 2 = g c d ( b , a % b ) bx_2 + (a \% b)y_2 = gcd(b,a\%b) bx2+(a%b)y2=gcd(b,a%b) -----(2)
其中: ( a % b ) = a − a / b ∗ b (a\%b) = a- a / b * b (a%b)=a−a/b∗b
根据欧几里得等式下一步推导得到:
a
x
1
+
b
y
2
=
b
x
2
+
(
a
−
a
/
b
∗
b
)
y
2
ax_1 + by_2 = bx_2 + (a- a / b * b)y_2
ax1+by2=bx2+(a−a/b∗b)y2
整理后得到:
a
x
1
+
b
y
1
=
a
y
2
+
b
(
x
2
−
[
a
/
b
]
∗
y
2
ax_1+ by_1 = ay_2 + b(x_2- [a / b] *y_2
ax1+by1=ay2+b(x2−[a/b]∗y2
通过上式,不难看出:
x
1
=
y
2
x_1 = y_2
x1=y2 ,
y
1
=
x
2
−
[
a
/
b
]
∗
y
2
y_1 = x_2 - [a / b] * y_2
y1=x2−[a/b]∗y2
到目前为止,我们得到了一个类似状态转移方程的东西,接下来只要代码实现就行:
int exgcd(int a,int b,int &x,int &y)
{
if(!b)
{
//最后一次迭代,联系到gcd可以明白
x = 1;y = 0;
return a;
}
int r = exgcd(b,a%b,x,y);
int t = x; x = y;
y = t-a/b*y;
//已经由公式推导
return r;
}
接下来,我们解析第二个应用:计算逆元 , 上面我们有提到通过费马小定理求解,但是费马小定理需要m为质数才有效果.而拓展欧几里得则没有这样的条件.
我们将 a x ≡ 1 ( m o d   m ) ax \equiv 1(mod\,m) ax≡1(modm)中的 x x x称为a的逆元,即ax取m的余数是1 -----(1)
带余除法的定义为: a = y m + r ( m 为 商 , r 为 余 ) a = ym + r (m为商 , r 为余) a=ym+r(m为商,r为余)----- (2)
(2)式如下替换
r → 1 r\rightarrow1 r→1 , a → a x \rightarrow ax →ax
则有 a − y m = 1 a - ym = 1 a−ym=1
我们便得到ax - ym = 1这个二次等式 , 将(-y) 替换成 y 则有
- a x + y m = 1 ax + ym = 1 ax+ym=1
这样,我们就能够通过欧几里得拓展求解了
以下是代码实现:
int mod_reverse(int a,int b)//a*x=1(mod b) 求a的逆元x
{
int d,x,y;
d=exgcd(a,b,x,y);
if(d==1)
return (x%b+b)%b;
//x可能是负数即不满足最小正整数的 , 通过+b再取模修正
else
return -1;
}
杨辉三角
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
int arr[100];
for(int i = 1; i <= n; i ++)
{
int sum = 1;
for(int j = 1; j <= i; j ++)
{
printf("%d\t",sum);
sum = sum * (i - j) / j;
}
cout << endl;
}
}
3、字符串处理
KMP算法
#include<bits/stdc++.h>
using namespace std;
string str , pat;
int len1 , len2 , next[100];
void getnext(){
memset(next , 0 , sizeof(next));
int i = 1 , j = 0;
while(i < len2)
{
if(pat[i] == pat[j])
{
next[i] = j + 1;
i ++; j ++;
}
else
{
if(j == 0)
i ++ && j ++;
else
j = next[j];
}
}
}
int find()
{
int i = 0 , j = 0;
while(i < len1 && j < len2)
{
if(str[i] == pat[j])
i ++ && j ++;
else
{
j = next[j] && i ++;
i ++;
}
}
if(j != len2)
return i - len2;
else
return -1;
}
int main(){
cin >> str >> pat;
len1 = str.length() , len2 = pat.length();
getnext();
int index = find();
cout << index;
}