数据结构
树状数组
二维树状数组
单点修改,子矩阵查询
//单点加 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);
}

浙公网安备 33010602011771号