算法汇总(随手记思路

由于没有大量时间写详细的博客,只作为学习时的思路统计,便于复习,别学完就忘了

倍增求LCA

建立祖先关系

parents[u][0] 表示 u 的 2 0 2^0 20 的祖先,即 u 的祖先
parents[u][1] 表示 u 的 2 1 2^1 21 的祖先,即父亲的父亲
parents[u][x] 表示 u 的 2 x 2^x 2x 的祖先,通过 2 的倍数做标记

form[u]表示当前点的祖先

获取各个节点的深度O(n)

BFS或者DFS即可

完善parents数组O(n*logn)

根据倍增的转移方程, f ( i , j ) f(i,j) f(i,j) 表示区间 [ i , i + 2 j − 1 ] [i,i+2^{j}-1] [i,i+2j1] 的答案
f ( i , j ) = 关系 ( f ( i , j − 1 ) , f ( i + 2 j − 1 , j − 1 ) ) f(i,j)=关系(f(i,j-1),f(i+2^{j-1},j-1)) f(i,j)=关系(f(i,j1),f(i+2j1,j1))

p a r e n t s [ i ] [ j ] = p a r e n t s [ p a r e n t s [ i ] [ j − 1 ] ] [ j − 1 ] parents[i][j] = parents[parents[i][j-1]][j-1] parents[i][j]=parents[parents[i][j1]][j1]

查询LCA(logu)

  1. 取深度较深的节点为 u,获得 u 能往上找的 2 x 2^x 2x的最大跨度
  2. 遍历跨度,从大到小,找到与 v 相同深度的祖先
  3. u 与 v 一起往上找,遍历跨度,从大到小,直到找到LCA。

代码

from https://blog.csdn.net/wjh2622075127/article/details/81060586

#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;

const int maxn = 10005;
int parents[maxn][20], depth[maxn];
int n, from[maxn], root = -1;
vector<int> G[maxn];

void init()
{
    memset(parents, -1, sizeof(parents));
    memset(from, -1, sizeof(from));
    memset(depth, -1, sizeof(depth));
}

void getData()
{
    cin >> n;
    int u, v;
    for (int i = 1; i < n; ++i) {
        cin >> u >> v;
        G[u].push_back(v);
        parents[v][0] = u;
        from[v] = 1;
    }
    for (int i = 1; i <= n; ++i) {
        if (from[i] == -1) root = i;
    }
}

void getDepth_dfs(int u)
{
    int len = G[u].size();
    for (int i = 0; i < len; ++i) {
        int v = G[u][i];
        depth[v] = depth[u] + 1;
        getDepth_dfs(v);
    }
}

void getDepth_bfs(int u)
{
    queue<int> Q;
    Q.push(u);
    while (!Q.empty()) {
        int v = Q.front();
        Q.pop();
        for (int i = 0; i < G[v].size(); ++i) {
            depth[G[v][i]] = depth[v] + 1;
            Q.push(G[v][i]);
        }
    }
}

void getParents()
{
    for (int up = 1; (1 << up) <= n; ++up) {
        for (int i = 1; i <= n; ++i) {
            parents[i][up] = parents[parents[i][up - 1]][up - 1];
        }
    }
}

int Lca(int u, int v)
{
    if (depth[u] < depth[v]) swap(u, v);
    int i = -1, j;
    while ((1 << (i + 1)) <= depth[u]) ++i;
    for (j = i; j >= 0; --j) {
        if (depth[u] - (1 << j) >= depth[v]) {
            u = parents[u][j];
        }
    }
    if (u == v) return u;
    for (j = i; j >= 0; --j) {
        if (parents[u][j] != parents[v][j]) {
            u = parents[u][j];
            v = parents[v][j];
        }
    }
    return parents[u][0];
}

void questions()
{
    int q, u, v;
    cin >> q;
    for (int i = 0; i < q; ++i) {
        cin >> u >> v;
        int ans = Lca(u, v);
        cout << ans << endl;
        //cout << u << " 和 " << v << " 的最近公共祖先(LCA)是: " << ans << endl; 
    }
}

int main()
{
    init();
    getData();
    depth[root] = 1;
    getDepth_dfs(root);
    //getDepth_bfs(root);
    getParents();
    questions();
}
/*
9
1 2
1 3
1 4
2 5
2 6
3 7
6 8
7 9
5
1 3
5 6
8 9
8 4
5 8
*/

求后缀数组

后缀数组,sa[i] 表示排名第 i 小的后缀

倍增,求LCA (具体知识点见上文

基数排序

DC3

后缀数组区间可加性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值