数据结构

树状数组

二维树状数组

单点修改,子矩阵查询

//单点加
void add(int x,int y,int v){
    for(int i=x;i<=n;i+=lowbit(i))
        for(int j=y;j<=m;j+=lowbit(j))
            c[i][j]+=v;
}
//查询子矩阵和
int sum(int x,int y){
    int res=0;
    for(int i=x;i>0;i-=lowbit(i))
        for(int j=y;j>0;j-=lowbit(j))res+=c[i][j];
    return res;
}
int ask(int x1,int y1,int x2,int y2){
    return sum(x2,y2)-sum(x1-1,y2)-sum(x2,y1-1)+sum(x1-1,y1-1);
}

 

 单点修改,查询全局第k小

权值数组:b[x]的值为x在a数组中出现的次数

该问题可离散化,如果原序列a的值域过大,可离散化后再建立权值数组b。

对于单点修改,只需将对原数列的单点修改转化为对权值数组的单点修改即可。

具体来说,原数组a[x]从y修改成z,转化为对权值数组b的单点修改就是b[y]单点减1,b[z]单点加1。

二分:时间复杂度是O(log 2n)

对于查询第k小,考虑二分x,查询权值数组中[1,x]的前缀和,找到x0使得[1,x0]的前缀和<k而[1,x0+1]的前缀和>=k,则第k大的数是x0+1。

 

倍增:时间复杂度是O(log n)

设x=0,sum=0,枚举i

· 查询权值数组中[x+1...x+2i]的区间和t

· 如果sum+t<k,扩展成功,x→x+2i,sum→sum+t;否则扩展失败,不操作

这样得到的x是满足[1...x]前缀和<k的最大值,所以最终答案是x+1。

int kth(int k){//对权值树状数组t查询第k小的数,n为数组t长度
    int sum=0,x=0;
    for(int i=log2(n);~i;--i){
        x+=1<<i;
        if(x>=n||sum+t[x]>=k)x-=1<<i;
        else sum+=t[x];
    }
    return x+1;
}

线段树

单点修改及区间和

int w[N];//每个数的值
struct Node{
    int l,r;
    int sum;//区间求和的值
}tr[4*N];

//pushup
void pushup(int u){
    tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}

//建树
void build(int u,int l,int r){
    if(l==r)tr[u]={l,r,w[r]};
    else{
        tr[u]={l,r};
        int mid=l+r>>1;
        build(u<<1,l,mid),build(u<<1|1,mid+1,r);
        pushup(u);
    }
};

//单点修改:将w[x]改为v
void modify(int u,int x,int v){
    if(tr[u].l==x&&tr[u].r==x)tr[u]={x,x,v};
    else{
        int mid=tr[u].l+tr[u].r>>1;
        if(x<=mid)modify(u<<1,x,v);
        else modify(u<<1|1,x,v);
        pushup(u);
    }
}

//求[l,r]区间和
int query(int u,int l,int r){
    if(tr[u].l>=l&&tr[u].r<=r)return tr[u].sum;
    else{
        int mid=tr[u].l+tr[u].r>>1;
        if(r<=mid)return query(u<<1,l,r);
        else if(l>mid)return query(u<<1|1,l,r);
        else{
            int ll=query(u<<1,l,r);
            int rr=query(u<<1|1,l,r);
            int res=ll+rr;
            return res;
        }
    }
}

 

区间修改及区间查询

int w[N];//每个数的值
struct Node{
    int l,r;
    int sum;//区间求和的值
    int add;//懒标记
}tr[4*N];

//pushup
void pushup(int u){
    tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}

//pushdown:除建树外,分裂都需要用
void pushdown(int u){
    Node &root=tr[u],&l=tr[u<<1],&r=tr[u<<1|1];
    l.add+=root.add,l.sum+=(l.r-l.l+1)*root.add;
    r.add+=root.add,r.sum+=(r.r-r.l+1)*root.add;
    root.add=0;
}

//建树
void build(int u,int l,int r){
    if(l==r)tr[u]={l,r,w[r],0};
    else{
        tr[u]={l,r};
        int mid=l+r>>1;
        build(u<<1,l,mid),build(u<<1|1,mid+1,r);
        pushup(u);
    }
};

//区间修改:区间[l,r]加d
void modify(int u,int l,int r,int d){
    if(tr[u].l>=l&&tr[u].r<=r){
        tr[u].sum+=(tr[u].r-tr[u].l+1)*d;//若范围过大,开ll
        tr[u].add+=d;
    }
    else{
        pushdown(u);
        int mid=tr[u].l+tr[u].r>>1;
        if(l<=mid)modify(u<<1,l,r,d);
        if(r>mid)modify(u<<1|1,l,r,d);
        pushup(u);
    }
}

//求[l,r]区间和
int query(int u,int l,int r){
    if(tr[u].l>=l&&tr[u].r<=r)return tr[u].sum;
    else{
        pushdown(u);
        int mid=tr[u].l+tr[u].r>>1;
        int res=0;
        if(l<=mid)res=query(u<<1,l,r);
        if(r>mid)res+=query(u<<1|1,l,r);
        return res;
    }
}

ST表

维护区间信息:区间最大,区间最小,区间按位和,区间按位或,区间gcd

RMQ( Range Maximum/Minimum Query ),求区间最大或最小值

void init(){
    for(int i=1;i<=n;++i)f[i][0]=a[i];
    for(int i=2;i<=n;++i)Log[i]=Log[i/2]+1;
    for(int i=1;i<=Log[n];++i)
        for(int j=1;j+(1<<i)-1<=n;++j)
            f[j][i]=max(f[j][i-1],f[j+(1<<(i-1))][i-1]);
}

 

int query(int l,int r){
    int len=Log[r-l+1];
    return max(f[l][len],f[r-(1<<len)+1][len]);
}

 

倍增

核心式子:to[x][i]=to[to[x][i-1]][i-1],代表从x开始跳2i步后的位置,先跳2i-1步,再跳2i-1

val[x][i]=val[x][i-1]+val[to[x][i-1]][i-1],表示从x开始跳2i步后得到的总值

    for(int i=1;i<=n;++i)val[i][0]=a[i];

    for(int i=1;i<=m;++i)
        for(int j=1;j<=n;++j){
            to[j][i]=to[to[j][i-1]][i-1];
            val[j][i]=val[j][i-1]+val[to[j][i-1]][i-1];
        }

 

 

倍增LCA

vector<int> g[N];//图
vector<int> dep(N);//深度
vector<vector<int>> f(N, vector<int>(20 + 5));//f[i][j]表示从i向祖先走2^j步

void dfs(int u) {
    for (int i = 0; i < g[u].size(); ++i) {
        int to = g[u][i];
        if (dep[to])continue;
        f[to][0] = u;
        dep[to] = dep[u] + 1;
        for (int j = 1; j <= 20; ++j)f[to][j] = f[f[to][j - 1]][j - 1];
        dfs(to);
    }
}

int lca(int a,int b){//求a和b的最近公共祖先
    if(dep[a]<dep[b])swap(a,b);
    for(int i=20;i>=0;--i)
        if(dep[f[a][i]]>=dep[b])a=f[a][i];
    if(a==b){//a走到b
        return a;
    }

    //a和b到同一层后,一起向祖先走
    for(int i=20;i>=0;--i){
        if(f[a][i]!=f[b][i]){
            a=f[a][i],b=f[b][i];
        }
    }
    return f[a][0];
}
int Dis(int a, int b) {
    return dep[a] + dep[b] - 2 * dep[lca(a, b)];
}

void init() {
    dep[1] = 1;//根为1
    dfs(1);
}

重链剖分

求lca

//fa(父节点)、sz(子树大小)、dep(深度)、
//hson(重子节点)、top(链头,所在重链中深度最小的节点)
int fa[N], sz[N], dep[N], hson[N], top[N];
//dfsn (节点的dfs序)、rdfsn(每棵子树的最大dfs序)
//int dfsn[N], rdfsn[N];
vector<int> edges[N];
//dfs1(1)
void dfs1(int p, int d = 1)
{
    int size = 1, ma = 0;
    dep[p] = d;
    for (auto q : edges[p])
        if (!dep[q])
        {
            dfs1(q, d + 1);
            fa[q] = p;
            size += sz[q];
            if (sz[q] > ma)
                hson[p] = q, ma = sz[q];
        }
    sz[p] = size;
}

// 需要先把根节点的top初始化为自身,top[1] = 1
void dfs2(int p)
{
    for (auto q : edges[p])
        if (!top[q])
        {
            if (q == hson[p])
                top[q] = top[p];
            else
                top[q] = q;
            dfs2(q);
        }
}
//O(logn)
int lca(int a, int b)
{
    while (top[a] != top[b])
    {
        if (dep[top[a]] > dep[top[b]])
            a = fa[top[a]];
        else
            b = fa[top[b]];
    }
    return (dep[a] > dep[b] ? b : a);
}
//初始化
void init() {
    for (int i = 1; i < N; ++i) {
        fa[i] = sz[i] = dep[i] = hson[i] = top[i] = 0;
        edges[i].clear();
    }
    dfs1(1);
    top[1] = 1;
    dfs2(1);
}

 

结合数据结构的重链剖分


//fa(父节点)、sz(子树大小)、dep(深度)、
//hson(重子节点)、top(链头,所在重链中深度最小的节点)
int fa[N], sz[N], dep[N], hson[N], top[N];
//dfsn (节点的dfs序)、rdfsn(每棵子树的最大dfs序)
int dfsn[N], rdfsn[N];
vector<int> edges[N];


//dfs1(1)
void dfs1(int p, int d = 1)
{
    int size = 1, ma = 0;
    dep[p] = d;
    for (auto q : edges[p])
        if (!dep[q])
        {
            dfs1(q, d + 1);
            fa[q] = p;
            size += sz[q];
            if (sz[q] > ma)
                hson[p] = q, ma = sz[q];
        }
    sz[p] = size;
}

int cnt;
// 需要先把根节点的top初始化为自身,top[1] = 1
void dfs2(int p)
{
    dfsn[p] = ++cnt;
    if (hson[p] != 0)
    {
        top[hson[p]] = top[p];
        dfs2(hson[p]);
    }
    for (auto q : edges[p])
        if (!top[q])
        {
            top[q] = q;
            dfs2(q);
        }
    rdfsn[p] = cnt;
}

int lca(int a, int b)
{
    while (top[a] != top[b])
    {
        if (dep[top[a]] > dep[top[b]])
            a = fa[top[a]];
        else
            b = fa[top[b]];
    }
    return (dep[a] > dep[b] ? b : a);
}
// 路径更新
void update_path(int x, int y, int z)
{
    while (top[x] != top[y])
    {
        if (dep[top[x]] > dep[top[y]])
        {
            update(dfsn[top[x]], dfsn[x], z);
            x = fa[top[x]];
        }
        else
        {
            update(dfsn[top[y]], dfsn[y], z);
            y = fa[top[y]];
        }
    }
    if (dep[x] > dep[y])
        update(dfsn[y], dfsn[x], z);
    else
        update(dfsn[x], dfsn[y], z);
}

// 路径查询
int query_path(int x, int y)
{
    int ans = 0;
    while (top[x] != top[y])
    {
        if (dep[top[x]] > dep[top[y]])
        {
            ans += query(dfsn[top[x]], dfsn[x]);
            x = fa[top[x]];
        }
        else
        {
            ans += query(dfsn[top[y]], dfsn[y]);
            y = fa[top[y]];
        }
    }
    if (dep[x] > dep[y])
        ans += query(dfsn[y], dfsn[x]);
    else
        ans += query(dfsn[x], dfsn[y]);
    return ans;
}

//子树更新
void update_subtree(int x, int z)
{
    update(dfsn[x], rdfsn[x], z);
}

//子树查询
int query_subtree(int x)
{
    return query(dfsn[x], rdfsn[x]);
}
//初始化
void init() {
    for (int i = 1; i < N; ++i) {
        fa[i] = sz[i] = dep[i] = hson[i] = top[i] = 0;
        dfsn[i] = rdfsn[i] = 0;
        edges[i].clear();
    }
    dfs1(1);
    top[1] = 1, cnt = 0;
    dfs2(1);
}

 

posted @ 2023-02-12 19:45  bible_w  阅读(31)  评论(0)    收藏  举报