数据结构专题
树状数组基础模板
单点修改+区间查询
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(mlogn)。
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]);
}
}
};

浙公网安备 33010602011771号