数据结构专题

树状数组基础模板

单点修改+区间查询

int n;
int a[N],c[N]; //对应原数组和树状数组

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

void add(int i,int k){    //在i位置加上k
    while(i <= n){
        c[i] += k;
        i += lowbit(i);
    }
}

int getsum(int i){        //求A[1 ~ i]的和
    int res = 0;
    while(i > 0){
        res += c[i];
        i -= lowbit(i);
    }
    return res;
}

int get(int x, int y) {     //求A[x ~ y]的和
    return getsum(y) - getsum(x - 1);
}

区间更新+单点查询

int n, m;
int a[N] = {0}, c[N]; //对应原数组和树状数组

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

void add(int i, int k) {    //在i位置加上k
    while (i <= n) {
        c[i] += k;
        i += lowbit(i);
    }
}

int getsum(int i) {        //求D[1 - i]的和,即A[i]值
    int res = 0;
    while (i > 0) {
        res += c[i];
        i -= lowbit(i);
    }
    return res;
}


//  A[x ~ y] 加k
void add1(int x, int y, int k) {
    add(x, k), add(y + 1,  -k);
}


void work() {
    for (int i = 1; i <= n; ++i) {
        add(i, a[i] - a[i - 1]);    //  维护差分
    }
}

区间加+区间查询

int n, m;
int a[N] = {0};
int sum1[N];    //(D[1] + D[2] + ... + D[n])
int sum2[N];    //(1*D[1] + 2*D[2] + ... + n*D[n])

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

void add(int i, int k) {
    int x = i;    //因为x不变,所以得先保存i值
    while (i <= n) {
        sum1[i] += k;
        sum2[i] += k * (x - 1);
        i += lowbit(i);
    }
}

int getsum(int i) {        //求前缀和A[1 ~ i]
    int res = 0, x = i;
    while (i > 0) {
        res += x * sum1[i] - sum2[i];
        i -= lowbit(i);
    }
    return res;
}


void work() {
    for (int i = 1; i <= n; ++i) {
        add(i, a[i] - a[i - 1]);
    }
}


//  [x, y]区间加k
void add1(int x, int y, int k) {
    add(x, k), add(y + 1, -k);
}


//  求[x, y]区间和
int get(int x, int y) {
    return getsum(y) - getsum(x - 1);
}

线段树模版

区间加、区间赋值、区间和、区间最值

class Segmentree {
private:
    struct node {
        int l, r;//区间[l,r]
        int add;//区间的延时标记
        int sum;//区间和
        int mx; //区间最大值
        int mn; //区间最小值
    } tree[MAXN << 2];//一定要开到4倍多的空间

    int arr[MAXN];//原序列

    void pushup(int index) {
        tree[index].sum = tree[index << 1].sum + tree[index << 1 | 1].sum;
        tree[index].mx = max(tree[index << 1].mx, tree[index << 1 | 1].mx);
        tree[index].mn = min(tree[index << 1].mn, tree[index << 1 | 1].mn);
    }

    void pushdown(int index) {
        //说明该区间之前更新过
        //要想更新该区间下面的子区间,就要把上次更新该区间的值向下更新
        if (tree[index].add) {//!!注:若区间赋值可能为0,此处改为if(tree[index].add != -1)
            //替换原来的值
            /*
            tree[index<<1].sum = (tree[index<<1].r-tree[index<<1].l+1)*tree[index].add;
            tree[index<<1|1].sum = (tree[index<<1|1].r-tree[index<<1|1].l+1)*tree[index].add;
            tree[index<<1].mx = tree[index].add;
            tree[index<<1|1].mx = tree[index].add;
            tree[index<<1].mn = tree[index].add;
            tree[index<<1|1].mn = tree[index].add;
            tree[index<<1].add = tree[index].add;
            tree[index<<1|1].add = tree[index].add;
            tree[index].add = 0;//!!注:若区间赋值可能为0,此处改为-1
            */
            //在原来的值的基础上加上val

            tree[index<<1].sum+=(tree[index<<1].r-tree[index<<1].l+1)*tree[index].add;
            tree[index<<1|1].sum+=(tree[index <<1|1].r-tree[index<<1|1].l+1)*tree[index].add;
            tree[index<<1].mx+=tree[index].add;
            tree[index<<1|1].mx+=tree[index].add;
            tree[index<<1].mn+=tree[index].add;
            tree[index<<1|1].mn+=tree[index].add;
            tree[index<<1].add+=tree[index].add;
            tree[index<<1|1].add+=tree[index].add;
            tree[index].add = 0;//!!注:若区间赋值可能为0,此处改为-1

        }
    }

    void build(int l, int r, int index) {
        tree[index].l = l;
        tree[index].r = r;
        tree[index].add = 0;
        //add刚开始一定要清空(!!注:若区间赋值可能为0,add初始化为-1(初始化应该赋为不会取到的值))
        if (l == r) {
//            tree[index].mn = tree[index].mx = tree[index].sum = arr[l];
            tree[index].mn = tree[index].mx = tree[index].sum = 0;
            return;
        }
        int mid = (l + r) >> 1;
        build(l, mid, index << 1);
        build(mid + 1, r, index << 1 | 1);
        pushup(index);
    }

public:
    //构造器
    Segmentree(){}
    //构造器1
    Segmentree(int l, int r) {
        //初始值为arr,下标从1开始
        for (int i = l; i <= r; ++i) arr[i] = 0;//初始值0,或者直接改build中的初始值
        build(l, r, 1);
    }

    //构造器2,arr <-- a
    Segmentree(int l, int r, vector<int> a) {
        for (int i = l; i <= r; ++i) {
            arr[i] = a[i];
        }
        build(l, r, 1);
    }

    void init(int n) {
        build(1, n, 1);
    }
    //区间修改
    void updata(int l, int r, int index, int val) {
        if (l <= tree[index].l && r >= tree[index].r) {
            /* 把原来的值替换成val
             * 因为该区间有tree[index].r-tree[index].l+1个数,所以区间和 以及 最值为:
            */
            /*tree[index].sum = (tree[index].r-tree[index].l+1)*val;
            tree[index].mn = val;
            tree[index].mx = val;
            tree[index].add = val;//延时标记*/


            /*在原来的值的基础上加上val
             * 因为该区间有tree[index].r-tree[index].l+1个数,所以区间和 以及 最值为:
             *
             */
            tree[index].sum += (tree[index].r - tree[index].l + 1) * val;
            tree[index].mn += val;
            tree[index].mx += val;
            tree[index].add += val;//延时标记

            return;
        }
        pushdown(index);

        int mid = (tree[index].l + tree[index].r) >> 1;
        if (l <= mid) {
            updata(l, r, index << 1, val);
        }
        if (r > mid) {
            updata(l, r, index << 1 | 1, val);
        }
        pushup(index);
    }

    //区间查询:和、最大、最小
    int query(int l, int r, int index) {
        if (l <= tree[index].l && r >= tree[index].r) {
            //return tree[index].sum;
            return tree[index].mx;
            //return tree[index].mn;
        }
        pushdown(index);
        int mid = (tree[index].l + tree[index].r) >> 1;
        int ans = 0;
        int Max = 0;
        int Min = inf;
        if (l <= mid) {
            ans += query(l, r, index << 1);
            Max = max(query(l, r, index << 1), Max);
            Min = min(query(l, r, index << 1), Min);
        }
        if (r > mid) {
            ans += query(l, r, index << 1 | 1);
            Max = max(query(l, r, index << 1 | 1), Max);
            Min = min(query(l, r, index << 1 | 1), Min);
        }
        //return ans;
        return Max;
        //return Min;
    }

    //区间查询最大,以及所在的最小位置
    node query1(int l, int r, int index) {
        if (tree[index].l == tree[index].r) return tree[index];
        pushdown(index);
        if (tree[index << 1].mx == tree[index].mx) return query1(l, r, index << 1);
        else return query1(l, r, index << 1 | 1);
    }
}st;

区间修改(替换)、区间修改(对x取gcd)、区间和

class SegmenTree {
private:
    struct node {
        int l, r;
        int add, sum, gd, lm;
    } tree[MAXN << 2];

    void pushup(int index) {
        tree[index].sum = tree[index << 1].sum + tree[index << 1 | 1].sum;
        tree[index].gd = gcd(tree[index << 1].gd, tree[index << 1 | 1].gd);
        tree[index].lm = lcm(tree[index << 1].lm, tree[index << 1 | 1].lm);
    }
    void pushdown(int index) {
        if (tree[index].add) {//!!注:add初始化为修改范围以外的值,则此处判断为add!=初始化的值
            //替换
            tree[index<<1].sum=tree[index].add*(tree[index<<1].r-tree[index<<1].l+1);
            tree[index<<1|1].sum=tree[index].add*(tree[index<<1|1].r-tree[index<<1|1].l+1);
            tree[index << 1].gd = tree[index << 1 | 1].gd = tree[index].add;
            tree[index << 1].lm = tree[index << 1 | 1].lm = tree[index].add;
            tree[index << 1].add = tree[index << 1 | 1].add = tree[index].add;
            tree[index].add = 0;//!!注:add修改为初始化的值
        }
    }
    void build(int l, int r, int index) {
        tree[index].l = l, tree[index].r = r;
        tree[index].add = 0;
        //!!注:add初始化为赋值范围以外的值,若赋值可能为0,需要修改初始化值,eg:-1
        if (l == r) {
            tree[index].sum = tree[index].gd = tree[index].lm = a[l];
            return;
        }
        int mid = (l + r) >> 1;
        build(l, mid, index << 1);
        build(mid + 1, r, index << 1 | 1);
        pushup(index);
    }

public:
    SegmenTree(){}
    SegmenTree(int l, int r) {
        build(l, r, 1);
    }
    void init(int l, int r) {
        build(l, r, 1);
    }
    //区间修改:替换成val
    void update(int l, int r, int index, int val) {
        if (l <= tree[index].l && tree[index].r <= r) {
            //区间替换
            tree[index].sum = val * (tree[index].r - tree[index].l + 1);
            tree[index].gd = tree[index].lm = val;
            tree[index].add = val;
            return ;
        }
        pushdown(index);
        int mid = (tree[index].l + tree[index].r) >> 1;
        if (l <= mid) update(l, r, index << 1, val);
        if (mid < r) update(l, r, index << 1 | 1, val);
        pushup(index);
    }
    //区间与val取gcd
    void modify_gcd(int l, int r, int index, int val) {
        if (val % tree[index].lm == 0) return ;//不用操作
        if (l <= tree[index].l && tree[index].r <= r && tree[index].gd == tree[index].lm) {
            //gcd与lcm相等说明这一段数全相等,可以一起取gcd
            val = gcd(val, tree[index].gd);
            tree[index].sum = val * (tree[index].r - tree[index].l + 1);
            tree[index].gd = tree[index].lm = val;
            tree[index].add = val;
            return ;
        }
        pushdown(index);
        int mid = (tree[index].l + tree[index].r) >> 1;
        if (l <= mid) modify_gcd(l, r, index << 1, val);
        if (mid < r) modify_gcd(l, r, index << 1 | 1, val);
        pushup(index);
    }
    //区间和
    int query(int l, int r, int index) {
        if (l <= tree[index].l && tree[index].r <= r) {
            return tree[index].sum;
        }
        pushdown(index);
        int mid = (tree[index].l + tree[index].r) >> 1, ans = 0;
        if (l <= mid) ans += query(l, r, index << 1);
        if (mid < r) ans += query(l, r, index << 1 | 1);
        return ans;
    }
}tr;

线段树(区间最小,区间0个数,区间最小个数)

const int MAXN = 1e6 + 5;
class Segmentree {
private:
    struct node {
        int l, r;//区间[l,r]
        int add;//区间的延时标记
        int sum;//区间和:最小值的个数
        int mn; //区间最小值
        int val;//0的个数
    } tree[MAXN << 2];//一定要开到4倍多的空间


    void pushup(int index) {
        tree[index].mn = min(tree[index << 1].mn, tree[index << 1 | 1].mn);
        tree[index].sum = (tree[index].mn == tree[index << 1].mn ? tree[index << 1].sum : 0)
                + (tree[index].mn == tree[index << 1 | 1].mn ? tree[index << 1 | 1].sum : 0);
        tree[index].val = tree[index << 1].val + tree[index << 1 | 1].val;
    }

    void pushdown(int index) {
        //说明该区间之前更新过
        //要想更新该区间下面的子区间,就要把上次更新该区间的值向下更新
        if (tree[index].add) {//!!注:若区间赋值可能为0,此处改为if(tree[index].add != -1)
            //替换原来的值
            /*

            tree[index<<1].mn = tree[index].add;
            tree[index<<1|1].mn = tree[index].add;
            tree[index<<1].add = tree[index].add;
            tree[index<<1|1].add = tree[index].add;
            tree[index << 1].val = (tree[index << 1].mn == 0 ? tree[index << 1].sum : 0);
            tree[index<<1|1].val=(tree[index<<1|1].mn==0?tree[index << 1 | 1].sum:0);

            tree[index].add = 0;//!!注:若区间赋值可能为0,此处改为-1
            */
            //在原来的值的基础上加上val
            tree[index << 1].mn += tree[index].add;
            tree[index << 1 | 1].mn += tree[index].add;
            tree[index << 1].add += tree[index].add;
            tree[index << 1 | 1].add += tree[index].add;

            tree[index << 1].val = (tree[index << 1].mn == 0 ? tree[index << 1].sum : 0);
            tree[index<<1|1].val=(tree[index<<1|1].mn==0?tree[index << 1 | 1].sum:0);

            tree[index].add = 0;//!!注:若区间赋值可能为0,此处改为-1

        }
    }

    void build(int l, int r, int index) {
        tree[index].l = l;
        tree[index].r = r;
        tree[index].add = 0;
        //add刚开始一定要清空(!!注:若区间赋值可能为0,add初始化为-1(初始化应该赋为不会取到的值))
        if (l == r) {
//            tree[index].mn = tree[index].mx = tree[index].sum = arr[l];
            tree[index].mn = 0;
            tree[index].sum = tree[index].val = 1;
            return;
        }
        int mid = (l + r) >> 1;
        build(l, mid, index << 1);
        build(mid + 1, r, index << 1 | 1);
        pushup(index);
    }

public:
    //构造器
    Segmentree(){}
    //构造器1
    Segmentree(int l, int r) {
        //初始值为arr,下标从1开始
        build(l, r, 1);
    }

    void init(int n) {
        build(1, n, 1);
    }
    //区间修改
    void updata(int l, int r, int index, int val) {
        if (l <= tree[index].l && r >= tree[index].r) {
            /* 把原来的值替换成val
             * 因为该区间有tree[index].r-tree[index].l+1个数,所以区间和 以及 最值为:
            */
            /*
            tree[index].mn = val;
            tree[index].val = (tree[index].mn == 0 ? tree[index].sum : 0);
            tree[index].add = val;//延时标记*/

            /*在原来的值的基础上加上val
             * 因为该区间有tree[index].r-tree[index].l+1个数,所以区间和 以及 最值为:
             *
             */
            tree[index].mn += val;
            tree[index].val = (tree[index].mn == 0 ? tree[index].sum : 0);
            tree[index].add += val;//延时标记

            return;
        }
        pushdown(index);

        int mid = (tree[index].l + tree[index].r) >> 1;
        if (l <= mid) {
            updata(l, r, index << 1, val);
        }
        if (r > mid) {
            updata(l, r, index << 1 | 1, val);
        }
        pushup(index);
    }

    //区间查询:0的个数
    int query(int l, int r, int index) {
        if (l <= tree[index].l && r >= tree[index].r) {
            return tree[index].val;
        }
        pushdown(index);
        int mid = (tree[index].l + tree[index].r) >> 1;
        int ans = 0;
        if (l <= mid) {
            ans += query(l, r, index << 1);
        }
        if (r > mid) {
            ans += query(l, r, index << 1 | 1);
        }
        return ans;
    }

}st;

线段树维护矩阵

对于一个循环序列,不能选择连续大于3个数,且带单点修改,问选择的数的和的最大值

struct node {
    //a[i][j]表示,l~r中选前缀i个和后缀j个的最大值
    //若要求为:不能选连续大于3个数,答案的限制为i+j<=3
    int l, r;
    int a[4][4];
};

int a[N];
class SegmenTree {
private:
    node tree[MAXN << 2];
    void pushup1(node & U, node & L, node & R) {
        int len = L.r - L.l + 1, ren = R.r - R.l + 1;
        for (int i = 0; i <= min(len, 3ll); ++i) {
            for (int j = 0; j <= min(ren, 3ll); ++j) {
                if (i == len && j == ren) {
                    if (i + j <= 3) {
                        U.a[i + j][i + j] = L.a[i][i] + R.a[j][j];
                    }
                } else if (i == len) {
                    for (int k = 0; i + k <= 3 && k <= ren; ++k) {
                        U.a[i + k][j] = L.a[i][i] + R.a[k][j];
                    }
                } else if (j == ren) {
                    for (int k = 0; j + k <= 3 && k <= len; ++k) {
                        U.a[i][j + k] = L.a[i][k] + R.a[j][j];
                    }
                } else {
                    int ma = 0;
                    U.a[i][j] = 0;
                    for (int k = min(3ll, len); k >= 0; --k) {
                        ma = max(ma, R.a[3 - k][j]);
                        U.a[i][j] = max(U.a[i][j], L.a[i][k] + ma);
                    }
                }
            }
        }
    }
    void pushup(int index) {
        int len = tree[index << 1].r - tree[index << 1].l + 1;
        int ren = tree[index << 1 | 1].r - tree[index << 1 | 1].l + 1;
        for (int i = 0; i <= min(len, 3ll); ++i) {
            for (int j = 0; j <= min(ren, 3ll); ++j) {
                if (i == len && j == ren) {
                    if (i + j <= 3) {
                    tree[index].a[i+j][i+j]=tree[index<<1].a[i][i]+tree[index<<1|1].a[j][j];
                    }
                } else if (i == len) {
                    for (int k = 0; i + k <= 3 && k <= ren; ++k) {
                    tree[index].a[i+k][j]=tree[index<<1].a[i][i]+tree[index<<1|1].a[k][j];
                    }
                } else if (j == ren) {
                    for (int k = 0; j + k <= 3 && k <= len; ++k) {
                    tree[index].a[i][j+k]=tree[index<<1].a[i][k]+tree[index<<1|1].a[j][j];
                    }
                } else {
                    int ma = 0;
                    tree[index].a[i][j] = 0;
                    //k需要从3开始,才能保证取的R中的max包含0~3-k
                    for (int k = 3; k >= 0; --k) {
                    ma = max(ma, tree[index << 1 | 1].a[3 - k][j]);
                    tree[index].a[i][j]=max(tree[index].a[i][j],tree[index<<1].a[i][k]+ma);
                    }
                }
            }
        }
    }
    void build (int l, int r, int index) {
        tree[index].l = l;
        tree[index].r = r;
        if (l == r) {
            tree[index].a[1][1] = a[l];
            return ;
        }
        int mid = (l + r) >> 1;
        build(l, mid, index << 1);
        build(mid + 1, r, index << 1 | 1);
        pushup(index);
    }

public:
    SegmenTree() {}
    void init(int l, int r) {
        build(l, r, 1);
    }
    void update(int l, int r, int index, int val) {
        if (l <= tree[index].l && r >= tree[index].r) {
            //替换
            tree[index].a[1][1] = val;
            return ;
        }
        int mid = (tree[index].l + tree[index].r) >> 1;
        if (l <= mid) update(l, r, index << 1, val);
        if (r > mid) update(l, r, index << 1 | 1, val);
        pushup(index);
    }
    //查询包含l~r的节点
    node query(int l, int r, int index) {
        if (l <= tree[index].l && r >= tree[index].r) {
            return tree[index];
        }
        int mid = (tree[index].l + tree[index].r) >> 1;
        if (r <= mid) return query(l, r, index << 1);
        if (l > mid) return query(l, r, index << 1 | 1);
        node L = query(l, r, index << 1), R = query(l, r, index << 1 | 1);
        node U;
        pushup1(U, L, R);
        return U;
    }

    //询问整个序列1~n,即查询根
    node get_root() {
        return tree[1];
    }
    
    //U.l~U.r的答案
    int get_max(node U) {
        int ans = 0;
        for (int i = 0; i <= 3; ++i) {
            for (int j = 0; j + i <= 3; ++j) {
                ans = max(ans, U.a[i][j]);
            }
        }
        return ans;
    }
}tr;

单点修改+区间最值

struct Segment_Tree {
    int mx[N << 2];

    Segment_Tree(){}
    void init() {
        memset(mx, 0, sizeof(mx));
        build(1, n, 1);
    }
    void pushup(int rt)
    {
        mx[rt] = max(mx[2 * rt], mx[2 * rt + 1]);
    }
    void build(int l, int r, int rt)
    {
        if (l == r)//叶节点赋值
        {
            mx[rt] = B[l];
            return;
        }
        int mid = (l + r) / 2;//递归建树——左子树,右子树
        build(l, mid, 2 * rt);
        build(mid + 1, r, 2 * rt + 1);
        pushup(rt);//更新父亲节点的值
    }
    int qurry(int x, int y, int l, int r, int rt)
    {
        //如果这个区间被完全包括在目标区间里面,直接返回这个区间的值
        if (x <= l && y >= r)
        {
            return mx[rt];
        }
        //pushdown(rt, r - l + 1);
        int mid = (l + r) / 2;
        int ret = 0;
        int ret1 = 0;
        //cout << l << " " << r << " " << mid << endl;
        if (x <= mid) ret = max(ret, qurry(x, y, l, mid, 2 * rt));
        //如果这个区间的左儿子和目标区间有交集那么搜索左儿子
        if (y > mid) ret1 = max(ret1, qurry(x, y, mid + 1, r, 2 * rt + 1));
        //如果这个区间的右儿子和目标区间有交集那么搜索右儿子
        return max(ret1, ret);
    }
    void update(int x, int c, int l, int r, int rt)
    {
        if (l == r)
        {
            mx[rt] = c;
            return;
        }
        int mid = (l + r) / 2;
        if (x <= mid)update(x, c, l, mid, 2 * rt);
        else update(x, c, mid + 1, r, 2 * rt + 1);
        pushup(rt);
    }
    
}tr;

线段树上二分

找到第一个大于等于k的位置

struct Segment_Tree {
    int lc[N << 2], rc[N << 2], maxn[N << 2];
    void pushup(int u) {maxn[u] = max(maxn[u << 1], maxn[u << 1 | 1]);}
    void build(int u, int l, int r) {
        lc[u] = l, rc[u] = r;
        if(l == r) {
            maxn[u] = MAXN;//0
            return;
        }
        build(u << 1, l, (l + r) >> 1), build(u << 1 | 1, (l + r >> 1) + 1, r);
        pushup(u);
    }
    int search(int u, int l, int k) {
        //Find the first element >= k in [l, r]
        //判断是否pos<=r
        if(lc[u] == rc[u]) {
            if(maxn[u] < k) return -1;
            return lc[u];
        }
        if(maxn[u] < k) return -1;
        if(rc[u] < l) return -1;
        //先搜左
        int res = search(u << 1, l, k);
        if(res != -1) return res;
        //再搜右
        return search(u << 1 | 1, l, k);
    }
    //单点修改
    void update(int u, int p, int va) {
        if(lc[u] == rc[u]) {
            maxn[u] = va;
            return;
        }
        if(p <= (lc[u] + rc[u] >> 1)) update(u << 1, p, va);
        else update(u << 1 | 1, p, va);
        pushup(u);
    }
}seg;

珂朵莉树

值相同且连续的数用一个区间表示,用set维护每个区间

每次对一个区间进行操作的时候,就将这个区间分离(split)出来。

而对于每一个查询操作,暴力求解。

数据随机 个数  个操作,均摊复杂度 O(mlog⁡n)

class odt_t {

private:

    struct node_t {
        int l, r; mutable int v;
        node_t(int l): l(l) {}
        node_t(int l, int r, int v): l(l), r(r), v(v) {}
        friend bool operator <(const node_t &a, const node_t &b)
        { return a.l < b.l; }
    };

    set<node_t> odt;

    //区间分裂成[l,x),[x,r]
    auto split(int x) {
        auto it = --odt.upper_bound(node_t(x));
        if (it->r < x) return next(it);//x没有与it相交
        if (it->l == x) return it;
        auto [l, r, v] = *it;
        odt.erase(it); odt.emplace(l, x - 1, v);
        return odt.emplace(x, r, v).first;
    }

    //区间分离出[l,r]
    auto get(int l, int r) {
        //注意先r+1,再l
        auto itr = split(r + 1), itl = split(l);
        return make_pair(itl, itr);
    }

public:
//    odt_t ot;
    //构造器
    odt_t(){}

    void init() {
        if (!odt.empty()) odt.erase(odt.begin(), odt.end());
    }

    //区间插入
    void insert(int l, int r, int v) {
        odt.emplace(l, r, v);
    }

    //区间推平
    void assign(int l, int r, int v) {
        auto [itl, itr] = get(l, r);
        odt.erase(itl, itr);
        odt.emplace(l, r, v);
    }

    //求差推平
    int assign_result(int l, int r, int v) {
        auto [itl, itr] = get(l, r);
        auto itb = itl; int res = 0;
        for (; itl != itr; ++itl) if (itl->v != v)
                res += (v - itl->v) * (itl->r - itl->l + 1);
        odt.erase(itb, itr), odt.emplace(l, r, v);
        return res;
    }

    //区间加
    void add(int l, int r, int v) {
        auto [itl, itr] = get(l, r);
        for (; itl != itr; ++itl) itl->v += v;
    }

    //区间第k大
    auto rank_k(int l, int r, int k) -> int {
        vector<pair<int, int>> bucket;
        auto [itl, itr] = get(l, r);
        for (; itl != itr; ++itl) bucket.push_back({itl->v, itl->r - itl->l + 1});
        sort(bucket.begin(), bucket.end());
        for (auto i : bucket) if ((k -= i.second) <= 0) return i.first;
        return -1;
    }

    void debug() {
        for (auto [l, r, v] : odt)
            cerr << "(" << l << " " << r << ": " << v << ")";
        cerr << endl;
    }

};

主席树

可以存下来数据结构的所有历史版本

核心思想:只记录每一个版本与前一个版本不同的节点, 并且利用历史版本之间的共用数据减少时间和空间消耗

查询a[l]~a[r]的第k小

#include<bits/stdc++.h>

using namespace std;
#define int long long
#define ull ::uint64_t
//#define ll long long
#define PII pair<int, int>
#define puu pair<ull, ull>
const int Mod = 998244353, N = 2e5 + 5, mod = 1e9 + 7, M = 4e7 + 5;
const int B = 233;
const double eps = 1e-6;
const int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

int root[N], idx;//每个版本的根节点 和 当前线段树中可用节点的下标

class SegmentTree{
private:
    struct Node  //可持久化线段树的信息
    {
        int l, r;
        int cnt;
    }tr[N * 4 + N * 17];//首先需要开N << 2个点, 需要操作n次, 因此需要开nlogn个
public:
    //建树
    int build(int l, int r)
    {
        int p = ++ idx;
        if (l == r) return p;
        int mid = l + r >> 1;
        tr[p].l = build(l, mid), tr[p].r = build(mid + 1, r);
        return p;
    }
    //插入
    int insert(int p, int l, int r, int x)
    {
        int q = ++ idx;
        tr[q] = tr[p];//复制之前的节点
        if (l == r)
        {
            tr[q].cnt ++ ;
            return q;
        }
        int mid = l + r >> 1;
        if (x <= mid) tr[q].l = insert(tr[p].l, l, mid, x);// 在左边
        else tr[q].r = insert(tr[p].r, mid + 1, r, x);// 在右边
        tr[q].cnt = tr[tr[q].l].cnt + tr[tr[q].r].cnt;// 更新 cnt = 左右儿子之和
        return q;
    }

    int query(int q, int p, int l, int r, int k)//后面一个版本为q, 前面一个版本为p;
    {
        if (l == r) return r;//到叶子节点
        int cnt = tr[tr[q].l].cnt - tr[tr[p].l].cnt;//左半边的个数
        int mid = l + r >> 1;
        //查询 正如前面分析所说
        if (k <= cnt) return query(tr[q].l, tr[p].l, l, mid, k);
        else return query(tr[q].r, tr[p].r, mid + 1, r, k - cnt);
    }
}tr;

vector<int> nums;
//求每个数离散化后的值为多少
int find(int x)
{
    return lower_bound(nums.begin(), nums.end(), x) - nums.begin();
}
void solve() {
    int n, m;
    cin >> n >> m;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
        nums.push_back(a[i]);
    }
    //离散化
    std::sort(nums.begin(), nums.end());
    nums.erase(std::unique(nums.begin(), nums.end()), nums.end());

    //第一个版本的线段树
    root[0] = tr.build(0, nums.size() - 1);

    for (int i = 1; i <= n; ++i) {
        root[i] = tr.insert(root[i - 1], 0, nums.size() - 1, find(a[i])); 
        //第i 个版本的线段树, 和上一个版本的线段树比较
    }
    while (m --) {
        int l, r, k;
        cin >> l >> r >> k;
        cout << nums[tr.query(root[r], root[l - 1], 0, nums.size() - 1, k)] << '\n';
    }

}

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int T = 1;
//    init();
//    cin >> T;
    while (T--) solve();
    return 0;
}

主席树维护子序列自动机

子序列自动机原理为:维护原序列的next[i][ch],表示从i位置开始ch出现的下一个位置

那么可以通过扫一遍目标序列即可知道是否为原序列的子序列

可以知道next数组的next[i]与next[i-1]只存在一处不相同,若ch的值域很大,开不下next数组,便可用主席树维护每个位置next[i]数组,即存下每个位置i开始ch出现的下一个位置

#include<bits/stdc++.h>

using namespace std;
#define int long long
#define ull ::uint64_t
//#define ll long long
#define PII pair<int, int>
#define puu pair<ull, ull>
const int Mod = 998244353, N = 1e5 + 5, mod = 1e9 + 7, M = 4e7 + 5;
const int B = 233;
const double eps = 1e-6;
const int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

int root[N], idx;//每个版本的根节点 和 当前线段树中可用节点的下标

class SegmentTree{
private:
    struct Node  //可持久化线段树的信息
    {
        int l, r;
        int go; // 该值出现的下一个位置
    }tr[N * 4 + N * 17];//首先需要开N << 2个点, 需要操作n次, 因此需要开nlogn个
public:
    //建树
    int build(int l, int r)
    {
        int p = ++ idx;
        if (l == r) return p;
        int mid = l + r >> 1;
        tr[p].l = build(l, mid), tr[p].r = build(mid + 1, r);
        return p;
    }
    //插入,p为上一版本的线段树,修改x的值为k
    int insert(int p, int l, int r, int x, int k)
    {
        int q = ++ idx;
        tr[q] = tr[p];//复制之前的节点
        if (l == r)
        {
            tr[q].go = k;
            return q;
        }
        int mid = l + r >> 1;
        if (x <= mid) tr[q].l = insert(tr[p].l, l, mid, x, k);
        else tr[q].r = insert(tr[p].r, mid + 1, r, x, k);
        return q;
    }

    int query(int u, int l, int r, int x)//查询x出现的下一个位置
    {
        if (l == r) return tr[u].go;
        int mid = l + r >> 1;
        if (x <= mid) return query(tr[u].l, l, mid, x);
        else return query(tr[u].r, mid + 1, r, x);
    }
}tr;


void solve() {
    int type, n, q, m;
    cin >> type >> n >> q >> m;
    //m为值域最大值
    vector<int> a(n + 1), b(n + 1);
    for (int i = 1; i <= n; ++i) cin >> a[i];
    root[n] = tr.build(1, m);
    // 维护f[i][j]:i位置后j出现的第一个位置
    // f[i]与f[i-1]只有f[i][a[i]]这个位置不同,从后往前维护每个版本
    for (int i = n; i >= 1; --i) {
        root[i - 1] = tr.insert(root[i], 1, m, a[i], i); // 由后一个版本复制而来
    }
    while (q --) {
        int k;
        cin >> k;
        for (int i = 1; i <= k; ++i) cin >> b[i];

        int now = 0;
        for (int i = 1; i <= k; ++i) {
            //不断查找b[i]出现的下一个位置,同时更新当前所在位置now
            now = tr.query(root[now], 1, m, b[i]);
            if (!now) break;
        }
        if (now) cout << "Yes\n";
        else cout << "No\n";
    }
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int T = 1;
//    init();
//    cin >> T;
    while (T--) solve();
    return 0;
}

Trie树

插入、查找、遍历

const int N = 2e5 + 5, M = 26;
class Trie {
private:
    int son[N][M], cnt[N], idx = 0;
    //N为所有串总长度,M为出现字符种类数,cnt标记每种串个数
public:
    Trie(){}
    void init() {
        for (int i = 0; i <= idx; ++i) {
            for (int j = 0; j < M; ++j) son[i][j] = 0;
            cnt[i] = 0;
        }
        idx = 0;
    }
    void insert(string s) {
        int pos = 0;
        for (int i = 0; i < s.size(); ++i) {
            int u = s[i] - 'a';
            if (!son[pos][u]) son[pos][u] = ++idx;
            pos = son[pos][u];
        }
        cnt[pos] ++;
    }
    //trie树中查找字符串s
    void find(string s) {
        int pos = 0;
        for (int i = 0; i < s.size(); ++i) {
            int u = s[i] - 'a';
            if (!son[pos][u]) return ;//没找到该串
            pos = son[pos][u];
        }
        if (cnt[pos]) return ;//找到了
        return ;//没找到
    }
    //dfs遍历trie树
    void dfs(int pos = 0) {
        for (int i = 0; i < M; ++i) {
            if (!son[pos][i]) return ;
            dfs(son[pos][i]);
        }
    }
};

 

posted @ 2024-07-14 23:17  bible_w  阅读(21)  评论(0)    收藏  举报