板子

图论

Tarjan

求强连通分量

int n, m, tot, top, cnt;
int dfn[N], low[N];
int q[N], ins[N], c[N];
vector<int> eg[N], scc[N], neg[N];
int cd[N];

void tarjan(int u){
  dfn[u] = low[u] = ++tot;
  q[++top] = u, ins[u] = 1;
  for(auto x : eg[u]){
    if(!dfn[x]){
      tarjan(x);
      low[u] = min(low[u], low[x]);
    }
    else if(ins[x]){
      low[u] = min(low[u], dfn[x]);
    }
  }
  if(dfn[u] == low[u]){
    int e; cnt++;
    do{
      e = q[top--], ins[e] = 0;
      c[e] = cnt, scc[cnt].push_back(e);
    }while(u != e);
  }
}

c[i] 表示第 i 个点的连通块编号, scc 储存每个连通块里的所有点

求割点

vector<int> g[N];
int n, m;
int tot, dfn[N], low[N], cnt;
int R, buc[N];

void tarjan(int u){
  dfn[u] = low[u] = ++tot;
  int son = 0;
  for(auto v : g[u]){
    if(!dfn[v]){
      son++, tarjan(v), low[u] = min(low[u], low[v]);
      if(low[v] >= dfn[u] && u != R)  cnt += !buc[u], buc[u] = 1;
    }
    else low[u] = min(low[u], dfn[v]);
  }
  if(son >= 2 && u == R) cnt += !buc[u], buc[u] = 1;
}

最短路

朴素Dijkstra

const int N = 505, M = 1e5 + 10;
int g[N][N], d[N], st[N];
int n, m;
int Dijkstra(){
  memset(d, 0x3f, sizeof d);
  d[1] = 0;
  for(int i = 1;i <= n;i ++){
    int tmp = 1e9, u;
    for(int i = 1;i <= n;i ++)
      if(!st[i] && tmp > d[i]) tmp = d[i], u = i;
    st[u] = 1;
    for(int i = 1;i <= n;i ++){
      d[i] = min(d[i], d[u] + g[u][i]);
    }
  }
  if(d[n] == 0x3f3f3f3f) return -1;
  return d[n];
}

堆优化Dijkstra

const int N = 1e6;
typedef pair<int, int> PII;
int h[N], e[N], dist[N], st[N], w[N], ne[N], idx;
int n, m;
void add(int a, int b, int z){
  e[idx] = b, ne[idx] = h[a], w[idx] = z, h[a] = idx++;
}
int Dijkstra(){
  memset(dist, 0x3f, sizeof dist);
  dist[1] = 0;
  priority_queue<PII, vector<PII>, greater<PII>> heap;
  heap.push({0, 1});
  while(heap.size()){
    PII tmp = heap.top();
    heap.pop();
    int u = tmp.second;
    if(st[u]) continue;
    st[u] = 1;
    for(int i = h[u];i != -1;i = ne[i]){
      int j = e[i];
      if(dist[j] > dist[u] + w[i]){
        dist[j] = dist[u] + w[i];
        heap.push({dist[j], j});
      }
    }
  }
  if(dist[n] == 0x3f3f3f3f) return -1;
  return dist[n];
}

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 v, w;
};

vector<edge> e[maxn];
int dis[maxn];
const int inf = 0x3f3f3f3f;

bool bellmanford(int n, int s) {
  memset(dis, 63, sizeof(dis));
  dis[s] = 0;
  bool flag;  // 判断一轮循环过程中是否发生松弛操作
  for (int i = 1; i <= n; i++) {
    flag = false;
    for (int u = 1; u <= n; u++) {
      if (dis[u] == inf) continue;
      // 无穷大与常数加减仍然为无穷大
      // 因此最短路长度为 inf 的点引出的边不可能发生松弛操作
      for (auto ed : e[u]) {
        int v = ed.v, w = ed.w;
        if (dis[v] > dis[u] + w) {
          dis[v] = dis[u] + w;
          flag = true;
        }
      }
    }
    // 没有可以松弛的边时就停止算法
    if (!flag) break;
  }
  // 第 n 轮循环仍然可以松弛时说明 s 点可以抵达一个负环
  return flag;
}

SPFA

struct edge {
  int v, w;
};

vector<edge> e[maxn];
int dis[maxn], cnt[maxn], vis[maxn];
queue<int> q;

bool spfa(int n, int s) {
  memset(dis, 63, sizeof(dis));
  dis[s] = 0, vis[s] = 1;
  q.push(s);
  while (!q.empty()) {
    int u = q.front();
    q.pop(), vis[u] = 0;
    for (auto ed : e[u]) {
      int v = ed.v, w = ed.w;
      if (dis[v] > dis[u] + w) {
        dis[v] = dis[u] + w;
        cnt[v] = cnt[u] + 1;  // 记录最短路经过的边数
        if (cnt[v] >= n) return false;
        // 在不经过负环的情况下,最短路至多经过 n - 1 条边
        // 因此如果经过了多于 n 条边,一定说明经过了负环
        if (!vis[v]) q.push(v), vis[v] = 1;
      }
    }
  }
  return true;
}

匈牙利求二分图最大匹配

int n, tag;
int e[N][N], use[N], path[N];

int dfs(int u){
  for(int i = 1; i <= n; i ++){
    if(e[u][i] && use[i] != tag){
      use[i] = tag;
      if(path[i] == -1 || dfs(path[i])){
        path[i] = u;
        return 1;
      }
    }
  }
  return 0;
}

int sum = 0;
for(int i = 1; i <= n; i ++){
  tag++;
  if(dfs(i)) sum++;
}

拓扑排序

void topu_sort(){
  queue<int> gs;
  for(int i = 1; i <= cnt; i ++){
    if(du[i] == 0){
      gs.push(i);
    }
  }
  while(gs.size()){
    int t = gs.front(); gs.pop();
    sx.push_back(t);
    for(auto x : ng[t]){
      du[x]--;
      if(du[x] == 0){
        gs.push(x);
      }
    }
  }
}

网络流

EK算法

时间复杂度: O(nm2) 不过一般是远远达不到这个上界的,能够处理 103104 规模的网络。

```cpp
#include<bits/stdc++.h>
using namespace std;

#define N 210
#define M 5010
#define int long long

int n, m, s, t;
int h[N], e[M * 2], ne[M * 2], w[M * 2], idx;
int g[N][N];
int maxf, pre[N], intc[N], vis[N];

void add(int a, int b, int c){
  e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}

int bfs(){
  queue<int> q;
  q.push(s);
  memset(vis, 0, sizeof vis);
  vis[s] = 1;
  intc[s] = 0x3f3f3f3f;
  while(!q.empty()){
    int u = q.front(); q.pop();
    for(int i = h[u]; i != -1; i = ne[i]){
      int v = e[i];
      if(vis[v] || w[i] == 0) continue;
      pre[v] = i;
      intc[v] = min(intc[u], w[i]);
      vis[v] = 1;
      q.push(v);
      if(v == t) return 1;
    }
  }
  return 0;
}

void up(){
  int now = t;
  while(now != s){
    int i = pre[now];
    w[i] -= intc[t];
    w[i ^ 1] += intc[t];
    now = e[i ^ 1];
  }
  maxf += intc[t];
}

signed main(){
  memset(h, -1, sizeof h);
//   freopen("shuju.in", "r", stdin);
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  cin >> n >> m >> s >> t;
  for(int i = 1; i <= m; i ++){
    int a, b, c;
    cin >> a >> b >> c;
    g[a][b] += c;
  }
  for(int i = 1; i <= n; i ++){
    for(int j = 1; j <= n; j ++){
      if(g[i][j]){
        add(i, j, g[i][j]), add(j, i, 0);
      }
    }
  }
  while(bfs()) up();
  cout << maxf;
}

Dinic

时间复杂度为 O(n2m) ,实际比这个快多了,一般能处理 104105 规模的网络。用Dinic求解二分图最大匹配的时间复杂度为 O(mn) ,同样实际会更快。

数学

矩阵快速幂

struct juzhen{
  int a[5][5];
  int n, m;
  juzhen(){                               //矩阵初始化
    n = m = 0;
    fill(a[0], a[0] + 5 * 5, 0);
  }
  void unit(int kn){                              //构建单位矩阵
    n = m = kn;
    for(int i = 1; i <= n; i ++) a[i][i] = 1;
  }
  void init(){                              //对于每个题目的初始矩阵
    n = 3, m = 1;
    a[1][1] = a[2][1] = a[3][1] = 1;
  }
  void zy(){                                 //每个题目的转移矩阵
    n = 3, m = 3;
    a[1][1] = a[1][3] = a[2][1] = a[3][2] = 1;
  }
  void out(){                                    //输出矩阵
    for(int i = 1; i <= n; i ++){
      for(int j = 1; j <= m; j ++){
        cout << a[i][j] << " ";
      }cout << endl;
    }
  }
  juzhen operator *(const juzhen &b){
    juzhen res;
    res.n = n, res.m = b.m;
    for(int i = 1; i <= n; i ++){
      for(int j = 1; j <= b.m; j ++){
        for(int k = 1; k <= m; k ++){
          res.a[i][j] = (res.a[i][j] + a[i][k] * b.a[k][j] % mo) % mo;
        }
      }
    }
    return res;
  }
};

juzhen qsm(juzhen base, int k){
  juzhen res;
  res.unit(3);
  while(k){
    if(k & 1) res = res * base;
    base = base * base;
    k >>= 1;
  }
  return res;
}

字符串

Hash

P3370 【模板】字符串哈希

#include<bits/stdc++.h>
using namespace std;

#define int long long
#define N 100010
#define mo1 1145141919811
#define mo2 200806040153
#define base 131  
#define x first
#define y second
typedef pair<int, int> PII;

int n;
PII a[N];
char s[N];

int hash1(char s[]){
  int n = strlen(s + 1), res = 0;
  for(int i = 1; i <= n; i ++){
    res = (res * base + (int)s[i]) % mo1;
  }
  return res;
}

int hash2(char s[]){
  int n = strlen(s + 1), res = 0;
  for(int i = 1; i <= n; i ++){
    res = (res * base + (int)s[i]) % mo2;
  }
  return res;
}


signed main(){
 // freopen("shuju.in", "r", stdin);
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  cin >> n;
  for(int i = 1; i <= n; i ++){
    cin >> s + 1;
    a[i].x = hash1(s);
    a[i].y = hash2(s);
  }
  sort(a + 1, a + n + 1);
  int ans = 1;
  for(int i = 2; i <= n; i ++){
    if(a[i].x != a[i - 1].x && a[i].y != a[i - 1].y){
      ans++;
    }
  }
  cout << ans;
  return 0;
}

本文作者:星影流灿

本文链接:https://www.cnblogs.com/yduck/p/17738040.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   星影流灿  阅读(7)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起