Skip to content

数据结构

树状数组

单点更新 区间查询

int a[maxn],c[maxn]; //原数组和树状数组

int lowbit(int x){ return x&(-x); }

void updata(int i,int k) //第 i 个元素加 k
{  
    while(i <= n)
    {
        c[i] += k;
        i += lowbit(i);
    }
}
int getsum(int i) //前i个和
{    
    int res = 0;
    while(i > 0)
    {
        res += c[i];
        i -= lowbit(i);
    }
    return res;
}

区间更新 单点查询

//利用原数组的差分数组建树
int a[maxn] = {0},c[maxn]; //原数组和树状数组

int lowbit(int x){ return x&(-x); }

void updata(int i,int k)  //第 i 个元素加 k
{   
   while(i <= n)
   {

       c[i] += k;
       i += lowbit(i);
   }
}

int getsum(int i) //前i个
{
    int res = 0;
    while(i > 0)
    {
        res += c[i];
        i -= lowbit(i);
    }
    return res;
}

/*
    updata(i,a[i] - a[i-1]);   //差分建树
    //[x,y]区间内加上k
    updata(x,k);         //A[x] - A[x-1]增加k
    updata(y+1,-k);      //A[y+1] - A[y]减少k
    //单点查询 差分建树所以单点查询变成了求和
    int sum = getsum(i);
*/

区间更新 区间查询

d 是 a 的差分数组

\(a_i = \sum_{j=1}^{i}{d_j}\)

$\therefore\sum_{i=1}^{r}{a_i} = \sum_{i=1}^{r}{\sum_{j=1}^{i}{d_j}} $

\(= \sum_{i=1}^{r}{d_i \times(r-i+1)}\)

​ $ = \sum_{i=1}^{r}{d_i \times(r+1)}-\sum_{i=1}^{r}{d_i \times i}$

所以维护2个树状数组 sum1[i] = d[i],sum2[i] = d[i]*(i-1)

线段树

/*区间和的线段树*/
//建树
void build(ll x, ll l, ll r) {
    if (l == r) {
        scanf("%lld", &sum[x]);
        return;
    }
    ll mid = (l + r) >> 1;
    build(x << 1, l, mid);
    build(x << 1 | 1, mid + 1, r);
    sum[x] = sum[x << 1] + sum[x << 1 | 1];
}
//下放懒标记
void pushdown(ll x, ll l, ll r) {
    ll mid = (l + r) / 2;
    lz[x << 1] += lz[x], lz[x << 1 | 1] += lz[x];
    sum[x << 1] += lz[x] * (mid - l + 1), sum[x << 1 | 1] += lz[x] * (r - mid);
    lz[x] = 0;
}
//区间更新
void update(ll x, ll l, ll r, ll gl, ll gr, ll k) {
    if (l >= gl && r <= gr) {
        lz[x] += k;
        sum[x] += (r - l + 1) * k;
        return;
    }
    pushdown(x, l, r);
    ll mid = (l + r) / 2;
    if (gl <= mid) update(x << 1, l, mid, gl, gr, k);
    if (gr > mid) update(x << 1 | 1, mid + 1, r, gl, gr, k);
    sum[x] = sum[x << 1] + sum[x << 1 | 1];
}
//区间和
ll get_sum(ll x, ll l, ll r, ll gl, ll gr) {
    if (l >= gl && r <= gr) return sum[x];
    pushdown(x, l, r);
    ll res = 0;
    ll mid = (l + r) / 2;
    if (gl <= mid) res += get_sum(x << 1, l, mid, gl, gr);
    if (gr > mid) res += get_sum(x << 1 | 1, mid + 1, r, gl, gr);
    return res;
}

并查集

int fa[MAXN];
//初始化
void init(int n){
    for(int i = 0; i < n; i++) fa[i] = i;  //父结点是自己
}
//查询并路径压缩
int find(int x){
    if(fa[x] != x) fa[x] = find(fa[x]);
    return fa[x];
}
//合并
void unite(int x, int y){
    int fx = find(x);
    int fy = find(y);
    fa[fx] = fy; 
}
//查询是否属于同一集合
bool same(int x, int y){
    return find(x) == find(y);
}

带权并查集

int fa[MAXN], value[MAXN]; //父结点  权值
//查询
int find(int x){
    if(x != fa[x]){
        int t = fa[x];
        fa[x] = find(fa[x]);
        value[x] += value[t]; 
        //这时的父亲结点的权值是父结点到根结点的权值
        //所以加上原本自己到父结点的权值就是自己到根结点的权值
    }
    return fa[x];
}
//合并, s是x->y的权值
void unite(int x, int y, int s){
    int px = find(x);
    int py = find(y);
    if(px != py){
        fa[px] = py;
        value[px] = s+value[y]-value[x];
        //x->y->py的权值 = x->px->py的权值,所以得px->py的权值为上式
    }  
}

单调栈

//求出每个元素左右第一个小于它的元素的下标
for(int i = 1; i <= n; i++)
{
    while(!st.empty() && num[st.top()] > num[i])
    {
        r[st.top()] = i;
        st.pop();
    }
    if(st.empty()) l[i] = -1;
    else if(num[st.top()] == num[i]) l[i] = l[st.top()];
    else l[i] = st.top();
    st.push(i);
}

while(!st.empty())
{
    r[st.top()] = n+1;
    st.pop();
}

可持久化数据结构

可持久化线段树(主席树)

洛谷 P3834

//一般开32倍空间
const int maxn = 2e5 + 5;
int a[maxn], b[maxn], n, m, tot, q;
int lc[maxn << 5], rc[maxn << 5], sum[maxn << 5], rt[maxn << 5];

void build(int &rt, int l, int r) {
    rt = ++tot, sum[rt] = 0;
    if (l == r) return;
    int mid = (l + r) >> 1;
    build(lc[rt], l, mid), build(rc[rt], mid + 1, r);
}

int update(int x, int l, int r, int t) {
    int xx = ++tot;
    lc[xx] = lc[x], rc[xx] = rc[x], sum[xx] = sum[x] + 1;
    if (l == r) return xx;
    int mid = (l + r) >> 1;
    if (t <= mid)
        lc[xx] = update(lc[xx], l, mid, t);
    else
        rc[xx] = update(rc[xx], mid + 1, r, t);
    return xx;
}

int query(int u, int v, int l, int r, int k) {
    int mid = (l + r) >> 1, t = sum[lc[v]] - sum[lc[u]];
    if (l == r) return l;
    if (k <= t) return query(lc[u], lc[v], l, mid, k);
    else return query(rc[u], rc[v], mid + 1, r, k - t);
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= m; i++) scanf("%d", a + i), b[i] = a[i];
    sort(b + 1, b + 1 + n);
    q = unique(b + 1, b + 1 + n) - b - 1;
    build(rt[0], 1, q);
    for (int i = 1; i <= n; i++) {
        int t = lower_bound(b + 1, b + 1 + q, a[i]) - b;
        rt[i] = update(rt[i - 1], 1, q, t);
    }
    int l, r, k;
    for (int i = 1; i <= m; i++) {
        scanf("%d %d %d", &l, &r, &k);
        printf("%d\n", b[query(rt[l - 1], rt[r], 1, q, k)]);
    }
    return 0;
}

二位前缀和

//预处理
for(int i = 1; i <= n; i++)
    for(int j = 1; j <= m; j++)
        dp[i][j] = dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+matrix[i][j];
//x1, y1, x2, y2 (查询区间)
int res = dp[x2][y2]-dp[x2][y1-1]-dp[x1-1][y2]+dp[x1-1][y1-1];