【董晓算法】竞赛常用知识点之数据结构1

 前言:

本系列是学习了董晓老师所讲的知识点做的笔记

董晓算法的个人空间-董晓算法个人主页-哔哩哔哩视频 (bilibili.com)

 动态规划系列(还没学完)

【董晓算法】动态规划之线性DP问题-CSDN博客

【董晓算法】动态规划之背包DP问题(2024.5.11)-CSDN博客

【董晓算法】动态规划之背包DP与树形DP-CSDN博客

字符串系列

【董晓算法】竞赛常用知识之字符串1-CSDN博客

【董晓算法】竞赛常用知识之字符串2-CSDN博客

并查集

P3367 【模板】并查集 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

fa初始化

每个节点指向它自己

fa[x]存节点x的父节点

  for(int i=1;i<=n;i++) fa[i]=i;

查找:

路径压缩,使树扁平化。 

int find(int x){
  if(fa[x]==x) return x;
  return fa[x]=find(fa[x]);
}

合并

把一个集合的根指向另一个集合的根

void unionset(int x,int y){
  fa[find(x)]=find(y);
}

判断是否在同一集合内

x=find(x),y=find(y);
if(x==y) puts("true")
else puts("false");

按秩合并

把小集合的根指向大集合的根。

void unionset(int x,int y){
  x=find(x),y=find(y);
  if(x==y)return;
  if(siz[x]>siz[y])swap(x,y);
  fa[x]=y;
  siz[y]+=siz[x];
}

线段树+懒标记

线段树是基于分治思想的二叉树,用来维护区间信息(区间和,区间最值,区间GCD等),可以在logn的时间内执行区间修改和区间查询。线段树中每个叶子节点存储元素本身非叶子节点存储区间内元素的统计值

递归建树

#define lc p<<1
#define rc p<<1|1
#define N 500005
int n, w[N];
struct Tree{ 
  int l,r,sum
}tr[N*4];
void build(int p,int l,int r){ //建树
  tr[p={l,r,w[l]};
  if(l==r) return;
  int m=l+r>>1;
  build(lc,l,m);
  build(rc,m+1,r);
  tr[p].sum=tr[lc].sum+tr[rc].sum;
 }

点修改

从根节点进入。递归找到叶子节点[x,x],把该节点的值增加k。然后从下往上更新其祖先节点上的统计值。

void update(int p,int k,intR){
  if(tr[p].1==x&&tr[p].r==x){
     tr[p].sum+=k;
     return;
   }
   int m=tr[p].1+tr[p].r>>1;//非叶子则裂开 
    if(x<=m)update(lc,x,k);
    if(x>m)update(rc,x,k);
    tr[p].sum=tr[lc].sum+tr[rc].sum;
}

区间查询

拆分与拼凑的思想。

例如,查询区间[4,9]可以拆分成 [4.5],[6.8〕 和 [9,9],通过合并这三个区间的答案求得查询答案。

int query(int p,int l,int r){ 
  if(l<=tr[p].l && tr[p].r<=r) return tr[p].sum;
  int m=tr[p].l+tr[p].r>>1;
  pushdown(p);
  int sum=0;
  if(l<=m) sum+=query(lc,l,r);
  if(r>m) sum+=query(rc,l,r);
  return sum;
}

区间修改

做懒惰修改,当 [x,y] 完全覆盖节点区间 [a.b]时先修改该区间的 sum 值再打上一个"懒标记"然后立即返回。等下次需要时再下传“懒标记",可以把每次修改和查询的时间都控制到O(logn)

void pushup(int u){ //向上更新
  tr[u].sum=tr[lc].sum+tr[rc].sum;
}
void pushdown(int p){ //向下更新
  if(tr[p].add){
    tr[lc].sum+=tr[p].add*(tr[lc].r-tr[lc].l+1),
    tr[rc].sum+=tr[p].add*(tr[rc].r-tr[rc].l+1),
    tr[lc].add+=tr[p].add,
    tr[rc].add+=tr[p].add,
    tr[p].add=0;      
  }
}
void update(int p,int k,intR){
  if(tr[p].1==x&&tr[p].r==x){//覆盖则修改
     tr[p].sum+=(tr[p].r-tr[p].l+1)*k;
     tr[p].add+=k;
     return;
   }
   int m=tr[p].1+tr[p].r>>1;//不覆盖则裂开 
     pushdown(p);
    if(x<=m)update(lc,x,k);
    if(x>m)update(rc,x,k);
    tr[p].sum=tr[lc].sum+tr[rc].sum;
    pushup(p);
}

 P3372 【模板】线段树 1 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值