ShanDongNormalUniversity huangkj

SDNU huangkj

头文件

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define all(s) s.begin(),s.end()
#define NO cout<<"NO"<<endl
#define YES cout<<"YES"<<endl
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<ll> vl;
typedef vector<vector<ll>> vll;
typedef map<int, int> mpii;
typedef map<ll, ll> mpll; 
typedef map<char, ll> mpcl;
typedef double lf;
typedef long double Lf;
const int MAXN = 2e5 + 5, MOD = 1e9 + 7, MAXP = 31, inf = 0x3f3f3f3f;
const ll INF = 1e18;
const double eps = 1e-6;

杂项

二分

整数二分
int calc(int x){
    int L = 0, R = n + 1;
    while(L + 1 < R){
        int M = (L + R) / 2;
        if(a[M] < x)
            L = M;
        else
            R = M;
    }
    return L;
}
实数二分
while(R - L < 1e-6){
    lf mid = (L + R) >> 1;
    if(check(mid)){
        r = mid;
    }else{
        l = mid;
    }
}

三分

while(R - L > eps){
    mid = (L + R) / 2;
    lmid = mid - esp;
    rmid = mid + esp;
    if(f(lmid) < f(rmid))
        r = mid;
    else
        l = mid;
}

数学

高精度

#include <cstdio>
#include <cstring>

constexpr int LEN = 1004;

int a[LEN], b[LEN], c[LEN], d[LEN];

void clear(int a[]){
    for(int i = 0; i < LEN; i++) a[i] = 0;
}

void read(int a[]){
    static char s[LEN + 1];
    scanf("%s",s);
    
    clear(a);
    
    int len = strlen(s);
    for(int i = 0; i < len; i++) a[len - i - 1] = s[i] - '0';
}

void print(int a[]){
    int i;
    for(i = LEN - 1; i >= 1; i--)
        if(a[i] != 0) break;
    for(;i >= 0; i--) putchar(a[i] + '0');
    putchar('\n');
}

void add(int a[], int b[], int c[]){
    clear(c);
    
    for(int i = 0; i < LEN - 1; i++){
        c[i] += a[i] + b[i];
        if(c[i] >= 10){
            c[i + 1] += 1;
            c[i] -= 10;
        }
    } 
}

void sub(int a[], int b[], int c[]){
    clear(c);
    
    for(int i = 0; i < LEN - 1; i++){
        c[i] += a[i] - b[i];
        if(c[i] < 0){
            c[i + 1] -= 1;
            c[i] += 10;
        }
    }
}
/*
如果a < b,那么就sub(b, a, c)在结果后面加负号.
*/

void mul(int a[], int b[], int c[]){
    clear(c);
    
    for(int i = 0; i < LEN -1; i++){
        for(int j = 0; j <= i; j++) c[i] += a[j] * b[i - j];
        
        if(c[i] >= 10){
            c[i + 1] += c[i] / 10;
            c[i] %= 10;
        }
    }
}

bool greater_eq(int a[], int b[], int last_dg, int len){
    if(a[last_dg + len] != 0) return true;
    for(int i = len - 1; i >= 0; i--){
        if(a[last_dg + i] > b[i]) return true;
        if(a[last_dg + i] < b[i]) return false;
    }
    return true;
}

void div(int a[], int b[], int c[], int d[]){
    clear(c);
    clear(d);
    
    int la, lb;
    for(la = LEN - 1; la > 0; la--)
        if(a[la - 1] != 0) break;
    for(lb = LEN - 1; lb > 0; lb--)
        if(b[lb - 1] != 0) break;
    if(lb == 0){
        puts("> <");
        return;
    }
    
    for(int i = 0; i < la; i++) d[i] = a[i];
    for(int i = la - lb; i >= 0; i--){
        while(greater_eq(d, b, i, lb)){
            for(int j = 0; j < lb; j++){
                d[i + j] -= b[j];
                if(d[i + j] < 0){
                    d[i + j + 1] -= 1;
                    d[i + j] += 10;
                }
            }
            c[i] += 1;
        }
    }
}

int main(){
    read(a);
    
    char op[4];
    scanf("%s",op);
    
    read(b);
    
    switch(op[0]){
        case '+' :
            add(a, b, c);
            print(c);
            break;
        case '-' :
            sub(a, b, c);
            print(c);
            break;
        case '*' :
            mul(a, b, c);
            print(c);
            break;
        case '/' :
            div(a, b, c, d);
            print(c);
            print(d);
            break;
        default:
            puts("> <");
    }
    
    return 0;
}

快速幂

实现
ll binpow(ll a, ll b){
	if(b == 0) return 1;
	ll res =binpow(a, b / 2);
	if(b % 2)
		return res * res * a;
	else
		return res * res;
}
ll binpow(ll a, ll b){
	ll res = 1;
	while(b > 0){
		if(b & 1) res = res * a;
		a = a * a;
		b >>= 1;
	}
	return res;
}

第二种实现方法是非递归式的。它在循环的过程中将二进制位为 1 时对应的幂累乘到答案中。尽管两者的理论复杂度是相同的,但第二种在实践过程中的速度是比第一种更快的,因为递归会花费一定的开销。

模意义下取幂
ll binpow(ll a, ll b, ll m){
	ll res = 1;
	while(b > 0){
		if(b & 1) res = res * a %m;
		a = a * a % m;
		b >>= 1;
	}
	return res;
}

这是一个非常常见的应用,例如它可以用于计算模意义下的乘法逆元。

既然我们知道取模的运算不会干涉乘法运算,因此我们只需要在计算的过程中取模即可。

数论

素数
实现
bool isPrime(int a){
	if(a < 2) return 0;
	if(a == 2) return 1;
	for(int i = 3;(ll)i * i <= a; i++)//防溢出
		if(a % i == 0) return 0;
	return 1;
} 
最大公约数
实现

欧几里得算法(Educlidean algorithm)

递归写法

//Version 1
int gcd(int a, int b){
	if(b == 0) return a;
	return gcd(b, a % b);
}
//Version 2
int gcd(int a, int b){ return b == 0 ? a : gcd(b, a % b);}

迭代写法

int gcd(int a, int b){
	while(b != 0){
		int tmp = a;
		a = b;
		b =tmp % b;
	}
	return a;
}
拓展欧几里得算法

扩展欧几里得算法(Extended Euclidean algorithm, EXGCD),常用于求 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)的一组可行解。

int Exgcd(int a, int b, int &x, int &y){
	if(!b){
		x = 1;
		y = 0;
		return a;
	}
	int d = Exgcd(b, a % b, x, y);
	int t = x;
	x = y;
	y = t- (a / b) * y;
	return d;
}

函数返回的值为gcd,在这个过程中计算x,y即可。

筛法

素数筛法
欧拉筛
bool isprime[MAXN]; // isprime[i]表示i是不是素数
int prime[MAXN]; // 现在已经筛出的素数列表
int n; // 上限,即筛出<=n的素数
int cnt; // 已经筛出的素数个数

void euler()
{
    memset(isprime, true, sizeof(isprime)); // 先全部标记为素数
    isprime[1] = false; // 1不是素数
    for(int i = 2; i <= n; ++i) // i从2循环到n(外层循环)
    {
        if(isprime[i]) prime[++cnt] = i;
        // 如果i没有被前面的数筛掉,则i是素数
        for(int j = 1; j <= cnt && i * prime[j] <= n; ++j)
        // 筛掉i的素数倍,即i的prime[j]倍
        // j循环枚举现在已经筛出的素数(内层循环)
        {
            isprime[i * prime[j]] = false;
            // 倍数标记为合数,也就是i用prime[j]把i * prime[j]筛掉了
            if(i % prime[j] == 0) break;
            // 最神奇的一句话,如果i整除prime[j],退出循环
            // 这样可以保证线性的时间复杂度
        }
    }
}

图论

图的存储

直接存边
#include <iostream>
#include <vector>

using namespace std;
struct Edge{
	int u, v;
};

int n, m;
vector<Edge> e;
vector<bool> vis;

bool find_edge(int u, int v){
	for(int i = 1; i <= m; i++){
		if(e[i].u == u && e[i].v == v){
			return 1;
		}
	}
}

void dfs(int u){
	if(vis[u]) return;
	vis[u] = 1;
	for(int i = 1; i <= m; i++){
		if(e[i].u == u){
			dfs(e[i].v);
		}
	}
}

int main(){
	cin >> n >> m;
	
	vis.resize(n + 1, 0);
	e.resize(m + 1);
	
	for(int i = 1; i <= m; i++)	cin >> e[i].u >> e[i].v;
	
	return 0;
}
邻接矩阵
#include <iostream>
#include <vector>

using namespace std;

int n, m;
vector<bool> vis;
vector<vector<bool>> adj;

bool find_edge(int u, int v) { return adj[u][b]; }

void dfs(int u){
	if(vis[u]) return;
	vis[u] = 1;
	for(int v = 1; v <= n; v++){
		if(adj[u][v]){
			dfs(v);
		}
	}
}

int main(){
	cin >> n >> m;
	
	vis.resize(n + 1);
	adj.resoze(n + 1, vector<bool>(n + 1));
	
	for(int i = 1 ;i <= m; i++){
		int u, v;
		cin >> u >> v;
		adj[u][v] = 1;
	}
	
	return 0;
}
邻接表
#include <iostream>
#include <vector>

using namespace std;

int n, m;
vector<bool> vis;
vector<vector<int>> adj;

bool find_edge(int u, int v){
	for(int i = 0; i < adj[u].size(); i++){
		if(adj[u][v] == v){
			return 1;
		}
	}
	return 0;
}

void dfs(int u){
	if(vis[u]) return;
	vis[u] = 1;
	for(int i = 1; i < adj[u].size(); i++)
		dfs(adj[u][v]);
}

int main(){
	cin >> n >> m;
	
	vis.resize(n + 1);
	adj.resize(n + 1);
	
	for(int i = 1; i <= m; i++){
		int u, v;
		cin >> u >> v;
		adj[u].push_back(v);
	}
	
	return 0;
}
链式前向星

核心代码

// head[u] 和 cnt 的初始值都为 -1
void add(int u, int v){
	next[++cnt] = head[u]; // 当前边的后继
	head[u] = cnt;         // 起点 u 的第一条边
	to[cnt] = v;           // 当前边的终点
}

// 遍历 u 的出边
for(int i = head[u]; ~i; i=nxt[i]){ // ~i 表示 i != -1
	int v = to[i];
}
#include <iostream>
#include <vector>

using namespace std;

int n, m;
vector<bool> vis;
vector<int> head, nxt ,to;

void add(int u, int v){
	nxt.push_back(head[u]);
	head[u] = to.size();
	to.push_back(v);
}

bool find_edge(int u, int v){
	for(int i = head[u]; ~i; i = nxt[i]){
		if(to[i] == v){
			return 1;
		}
	}
	return 0;
}

void dfs(int u){
	if(vis[u]) return;
	vis[u] = 1;
	for(int i = head[u]; ~i; i = nxt[i])
		dfs(to[i]);
}

int main(){
	cin >> n >> m;
	
	vis.resize(n + 1, 0);
	head.resize(n + 1, -1);
	
	for(int i = 1; i <= m; i++){
		int u, v;
		cin >> u >> v;
		add(u, v);
	}
	
	return 0;
}

DFS

栈实现
vector<vector<int>> adj; // 邻接表
vector<bool> vis;        // 记录节点是否已经遍历

void dfs(int s){
	stack<int> st;
	st.push(s);
	vis[s] = 1;
	
	while(!st.empty()){
		int u = st.top();
		st.pop();
		
		for(int v : adj[u]){
			if(!vis[v]){
				vis[v] = 1; // 确保栈里无重复元素
				st.push(v);
			}
		}
	}
}
递归实现

邻接表作为图的存储方式:

vector<vector<int>> adj;
vector<bool> vis;

void dfs(const int u){
    vis[u] = 1;
    for(int v : adj[u])
        if(!vis[v]) 
            dfs(v);
}

链式前向星作为图的存储方式:

void dfs(int u){
    vis[u] = 1;
    for(int i = head [u]; i; i=e[i].x){
        if(!vis[e[i].t]){
            dfs(v);
        }
    }
}

BFS

实现

基于链式前向星的存图方式:

void vfs(int u){
    while(!Q.empty())
        Q.pop();
    vis[u] = 1;
    d[u] = 0;
    p[u] = -1;
    while(!Q.empty()){
        u = Q.front();
        Q.pop();
        for(int i = head[u]; i; i = e[i].nxt){
            if(!vis[e[i].to]){
                Q.push(e[i].to);
                vis[e[i].to] = 1;
                d[e[i].to] = d[u] + 1;//节点深度
                p[e[i].to] = u;//前节点
            }
        }
    }
}

void restore(int x){
    vector<int> res;
    for(int v = x; v != -1; v = p[v]){
        res.push_back(v);
    }
    std::reverse(res.begin(),res.end());//逆转容器内元素
    for(int i = 0; i < res.size(); i++)
        	cout << res[i];
}

最短路

Floyd算法(使用任何图,不能有负环)
for(k = 1; k <= n; k++){
	for(x = 1; x <= n; x++){
        for(y = 1; y <= n; y++){
            f[x][y] = min(f[x][y],f[x][k] + f[k][y])
        }
    }
}
Bellman-Ford算法
struct Edge {
    int u, v, w;
};

vector<Edge> edge;

int dis[MAXN], u, v, w;
constexpr int INF = 0x3f3f3f3f;

bool bellmanford(int n, int s){
    memset(dis, 0x3f, (n + 1) * sizeof(int));
    dis[s] = 0;
    bool flag = 0;//判断一轮循环是否发生松弛操作
    for(int i = 1; i <= n; i++){
        flag = 0;
        for(int j = 0; j < edge.size(); j++){
            u = edge[j].u, v = edge[j].v, w = edge[j].w;
            if(dis[u] == INF) continue;
            if(dis[v] > dis[u] + w){
                dis[v] = dis[u] + w;
                flag = 1;
            }
        }
        //没有可以松弛的边时就停止算法
        if(!flag){
            break;
        }
    }
    // 第 n 轮循环仍然可以松弛时说明 s 点可以抵达一个负环
    return flag;
}
Dijkstra算法(非负权边)
struct edge {
    int v, w;
};

vector<edge> e[MAXN];
int dis[MAXN], vis[MAXN];

void dijkstra(int n, int s){
    memset(dis, 0x3f, (n + 1) * sizeof(int));
    dis[s] = 0;
    for(int i = 1; i <= n; i++){
        int u = 0; mind =0x3f3f3f3f;
        for(int j = 1; j <= n; j++)
            if(!vis[j] && dis[j] < mind)
                u = j, mind = dis[j];
        vis[u] = 1;
        for(auto ed : e[u]){
            int v = ed.v, w=ed,w;
            if(dis[v] > dis[u] + w)
                dis[v] = dis[u] + w;
        }
    }
}

拓扑排序

Kahn算法
int n,m;
vector<int> G[MAXN];
int in[MAXN]; // 存储每个结点的入度

bool toposort(){
    vector<int> L;
    queue<int> S;
    for(int i = 1;i <= n;i++)
        if(in[i] == 0) S.push(i);
    while(!S.empty()){
        int u = S.front();
        S.pop();
        L.push_back(u);
        for(auto v : G[u]){
            if(--in[v] == 0){
                S.push(v);
            }
        }
    }
    if(L.size() == n){
        for(auto i : L) cout << i << ' ';
     	return true;
    }
    return false;
}

数据结构

并查集

普通并查集模板
#include <bits/stdc++.h>
using namespace std;
struct DSU{
	vector<int> fa,rk;
	int n;
	void init(int x){ //一共n个节点
		n = x;
		rk = fa = vector<int>(n+1); // 开数组下标为0到n
		for(int i = 1; i <= n ;++i)fa[i] = i ,rk[i] = 1; //初始化,每个集合只有一个元素,每个元素都是根,每个集合的大小为1
 	}
int find(int x){return fa[x]==x?x:fa[x] = find(fa[x]);}
/*
int find(int x){
	if(x==fa[x]){
	return x; // 为根直接返回
	}
	int root = find(fa[x]);
	fa[x] = root; // 这里是路径压缩
	return root;
*/
	void merge(int u,int v){
		int x = find(u),y = find(v); //找到两个集合的根节点
		if(x==y)return;//两个元素在同一个集合
		if(rk[x]>rk[y])swap(x,y); //按秩合并优化 , 用小的集合连在大的集合下面
		fa[x] = y;
		rk[y] += rk[x];
	}
};
int main(){
	int n,m,p;cin>>n>>m>>p;
	DSU dsu;dsu.init(n);
	for(int u,v;m;m--)cin>>u>>v,dsu.merge(u,v);
	for(int u,v;p;p--){
		cin>>u>>v;
		cout<<(dsu.find(u)==dsu.find(v)?"Yes":"No")<<'\n';
	}
	return 0;
}
种类并查集
#include "bits/stdc++.h"
using namespace std;
struct DSU{
	vector<int> fa,rk;
	int n;
	void init(int x){ //一共n个节点
	n = x;
	rk = fa = vector<int>(n+1); // 开数组下标为0到n
	for(int i = 1; i <= n ;++i)fa[i] = i ,rk[i] = 1; //初始化,每个集合只有一个元素,每个元素都是根,每个集合的
大小为1
 }
int find(int x){return fa[x]==x?x:fa[x] = find(fa[x]);}
	void merge(int u,int v){
		int x = find(u),y = find(v); //找到两个集合的根节点
		if(x==y)return;//两个元素在同一个集合
		if(rk[x]>rk[y])swap(x,y); //按秩合并优化 , 用小的集合连在大的集合下面
		fa[x] = y;
		rk[y] += rk[x];
	}
};
int main(){
	int n,m;cin>>n>>m;
	DSU dsu;dsu.init(2*n);
	for(int u,v;m;m--){
		char op;cin>>op;
		cin>>u>>v;
		if(op=='F'){
			dsu.merge(u,v); // 和朋友 做朋友
		}else{
			dsu.merge(u,v+n); // u和 敌人的敌人 做朋友, 并且 v+n 和 u 相连,u也成为了v的敌人
			dsu.merge(v,u+n);
		}
	}
	vector<int> vis(2*n+1);
	int ans = 0;
	for(int i = 1; i <= n ;++i){
		int v = dsu.find(i);
		if(vis[v]==0){ //每个集合对答案有1的贡献
			vis[v]=1;
			ans++;
 		}
	 }
	cout<<ans;
	return 0;
}

二叉堆
向上调整/向下调整
void up(int x){
    while(x > 1 && h[x] > h[x / 2]){
        swap(h[x],h[x / 2]);
        x /= 2;
    }
}

void down(int x){
    while(x * 2 <= n){
        t = x * 2;
        if(t + 1 <= n && h[t + 1] > h[t]) t++;
        if(h[t] <= h[x]) break;
        swap(h[x],h[t]);
        x = t;
    }
}
建堆
//向上调整
//从根开始,按BFS序进行。
void build_heap_1(){
	for(i = 1;i <= n;i++) up(i);
}

//向下调整
//从叶子开始
void build_heap_2(){
    for(i = n;i >= 1;i--) down(i);
}
对顶堆
priority_queue<int,vector<int>> q1;//大根堆
priority_queue<int,vector<int>,greater<int>> q2;//小根堆
void duidingdui(int n){
    int x;
    cin >> x;
    q1.push(x);
    for(int i = 2;i <= n;i++){
        cin >> x;
        if(x > q1.top()) q2.push(x);
        else q1.push(x);
        while(abs( (int)q1.size() > (int)q2.size() ) > 1){
            if( (int)q1.size() > (int)q2.size() ){
                q2.push(q1.top());
                q1.pop();
            }else{
                q1.push(q2.top());
                q2.pop();
            }
        }
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值