# data structure

## 1. Consolidation

• Merge two sets
• Query the ancestor node of an element
• Ask if two elements are in the same collection

It has information transmission and symmetry

// Simple parallel search set:

int p[N]; //Store the ancestor node of each point

// Return the ancestor node of x + path compression
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}

// Initialization, assuming that the node number is 1~n
for (int i = 1; i <= n; i ++ ) p[i] = i;

// Merge the two sets of a and b:
p[find(a)] = find(b);


### Optimization mode

• Path compression O ( log ⁡ n ) O(\log n) O(logn)
• Merge by rank O ( log ⁡ n ) O(\log n) O(logn)
• Both O ( α ( n ) ) O(\alpha(n)) O(α(n))

### Weighted union search set

• Record the size of each collection and bind it to the root node
• The distance (relative distance) from each node to the root node is bound to each element
// Maintain and query the set size:

int p[N], size[N];
//p [] stores the ancestral node of each point. size [] only the meaningful of the ancestral node, indicating the number of points in the set of ancestral nodes

// Returns the ancestor node of x
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}

// Initialization, assuming that the node number is 1~n
for (int i = 1; i <= n; i ++ )
{
p[i] = i;
size[i] = 1;
}

// Merge the two sets of a and b:
size[find(b)] += size[find(a)];
p[find(a)] = find(b);

// Maintain the distance to the ancestral node and query the set:

int p[N], d[N];
//P [] stores the ancestor node of each point, and d[x] stores the distance from X to p[x]

// Returns the ancestor node of x
int find(int x)
{
if (p[x] != x)
{
int root = find(p[x]);
d[x] += d[p[x]];
p[x] = root;
}
return p[x];
}

// Initialization, assuming that the node number is 1~n
for (int i = 1; i <= n; i ++ )
{
p[i] = i;
d[i] = 0;
}

// Merge the two sets of a and b:
p[find(a)] = find(b);
d[find(a)] = distance; // Initialize the offset of find(a) according to the specific problem


### Expand domain and query set (enumeration)

1250. Grid game - AcWing question bank

Parallel search set solves the problems of connectivity (undirected graph connectivity component) and transitivity (genealogical relationship), and can be maintained dynamically. Without looking at the lattice, in any graph, an edge is added to form a ring. If and only if the two points connected by this edge have been connected, the points can be divided into several sets, and each set corresponds to a connected block in the graph.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;

const int MAXN = 40050;
int n, m, res = 1e9;
int a[MAXN], fa[MAXN];

int transfer(int x, int y)	// Convert 2D to 1D
{
return (x-1) * n + y;
}

void init()
{
for(int i = 1;i <= n*n;i++)
{
fa[i] = i;
}
}

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

int main()
{
scanf("%d%d",&n,&m);
init();
for(int i = 1;i <= m;i++)
{
int x, y;
char op;
scanf("%d %d %c",&x,&y,&op);
int res1, res2;

res1 = transfer(x,y);
if(op == 'D') res2 = transfer(x+1,y);
else if(op == 'R') res2 = transfer(x,y+1);

int u = find(res1), v = find(res2);
if(u == v) {
res = min(res,i);
}
else {
fa[u] = v;
}
}
if(res != 1e9) printf("%d\n",res);
else puts("draw");
return 0;
}


1252. Matching purchase - AcWing question bank

Combined search + 01 Backpack

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
const int MAXN = 10010;
int fa[MAXN], c[MAXN], d[MAXN], dp[MAXN];
int n, m, w, id;

struct node{
int c, d;
}Node[MAXN];

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

void init()
{
for(int i = 1;i <= n;i++) fa[i] = i;
}

int main()
{
scanf("%d%d%d",&n,&m,&w);
init();
for(int i = 1;i <= n;i++)
{
scanf("%d%d",&c[i],&d[i]);
}
for(int i = 1;i <= m;i++)
{
int a, b;
scanf("%d%d",&a,&b);
int u = find(a), v = find(b);

if(u != v)
{
fa[v] = u;
c[u] += c[v];
d[u] += d[v];
}
}

for(int i = 1;i <= n;i++) {
if(i == fa[i])
{
Node[id].c = c[i];
Node[id].d = d[i];
id++;
}
}

for(int i = 0;i < id;i++)
{
for(int j = w;j >= Node[i].c;j--)
{
dp[j] = max(dp[j],dp[j-Node[i].c]+Node[i].d);
}
}
int ans = dp[w];
printf("%d\n",ans);
return 0;
}


237. Automatic program analysis - AcWing question bank

Joint search set + discretization

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>

using namespace std;
typedef long long ll;
const int MAXN = 2e6+50;

int n, T, id, cnt;
int fa[MAXN];

vector<pair<int,int>>equ, iequ;

int all[MAXN];

void init()
{
for(int i = 0;i <= cnt;i++) fa[i] = i;
}

int search(int x)
{
int res = lower_bound(all,all+cnt,x)-all;
return res;
}

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

int main()
{
scanf("%d",&T);
while(T--)
{
equ.clear();
iequ.clear();
bool flag = true;
scanf("%d",&n);
for(int k = 0;k < n;k++)
{
int i, j, e;
scanf("%d%d%d",&i,&j,&e);
if(e == 1) equ.push_back(make_pair(i,j));
else iequ.push_back(make_pair(i,j));
all[id++] = i;
all[id++] = j;
}
sort(all,all+id);
cnt = unique(all,all+id)-all;
init();
for(auto i : equ)
{
int x = search(i.first), y = search(i.second);
int u = find(x), v = find(y);
fa[u] = v;
}
for(auto i : iequ)
{
int x = search(i.first), y = search(i.second);
int u = find(x), v = find(y);
if(u == v) {
flag = false;
break;
}
}
if(flag) puts("YES");
else puts("NO");
}
return 0;
}


### Union search set with edge weight

239. Parity game - AcWing question bank

Using the idea of prefix and, S i = a 1 + a 2 + ⋯ + a i S_i = a_1 + a_2 + \dots + a_i Si​=a1​+a2​+⋯+ai​

if S [ L , R ] S[L,R] If there are odd ones in S[L,R], then S R S_R SR # and S L − 1 S_{L-1} SL − 1 − different parity

if S [ L , R ] S[L,R] If there is an even number of 1 in S[L,R], then S R S_R SR # and S L − 1 S_{L-1} SL − 1 has the same parity

Using discretization, we only need to find out the first contradiction

Weighted concurrent query set: d [ x ] = 0 d[x]=0 d[x]=0 indicates the same class as the parent node, and 1 indicates different classes. It describes a relative relationship. (by taking the mold of 2)

There are several kinds of modules, and the relationship between the two points and the root node is judged y by the length of the two points.

Judge the relationship between x and y, ( d [ x ] + d [ y ] ) m o d 2 = 0 (d[x]+d[y])mod2 = 0 (d[x]+d[y])mod2=0 description x x x and y y y congener

① x, y are of the same class

• f a [ x ] = f a [ y ] fa[x]=fa[y] fa[x]=fa[y] ( d [ x ] + d [ y ] ) m o d    2 = 0 (d[x]+d[y]) \mod 2= 0 (d[x]+d[y])mod2=0 is not contradictory ( d [ x ] + d [ y ] ) m o d    2 = 1 (d[x]+d[y])\mod 2= 1 (d[x]+d[y])mod2=1 contradiction
• f a [ x ] ≠ f a [ y ] fa[x] \ne fa[y] fa[x]​=fa[y] d [ f a [ x ] ] = ( d [ x ] + d [ y ] ) m o d    2 d[fa[x]] = (d[x] + d[y]) \mod 2 d[fa[x]]=(d[x]+d[y])mod2

① x, y are different

• f a [ x ] = f a [ y ] fa[x]=fa[y] fa[x]=fa[y] ( d [ x ] + d [ y ] ) m o d    2 = 1 (d[x]+d[y]) \mod 2= 1 (d[x]+d[y])mod2=1 no contradiction ( d [ x ] + d [ y ] ) m o d    2 = 0 (d[x]+d[y])\mod 2= 0 (d[x]+d[y])mod2=0 contradiction
• f a [ x ] ≠ f a [ y ] fa[x] \ne fa[y] fa[x]​=fa[y] d [ f a [ x ] ] = ( d [ x ] + d [ y ] + 1 ) m o d    2 d[fa[x]] = (d[x] + d[y] + 1) \mod 2 d[fa[x]]=(d[x]+d[y]+1)mod2

239. Parity game - AcWing question bank

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>

using namespace std;
const int MAXN = 10010;
int n, m;
int fa[MAXN], d[MAXN];
vector<int>all;
struct node{
int l, r, flag;
}N[MAXN];
vector<node>v;

int find(int x)
{
if(x != fa[x]) {
int root = find(fa[x]);
d[x] += d[fa[x]];
fa[x] = root;
}
return fa[x];
}

int search(int x)
{
return lower_bound(all.begin(),all.end(),x)-all.begin();
}

int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= m;i++)
{
int l, r;
char op[7];
scanf("%d%d%s",&l,&r,op);
if(*op == 'e') v.push_back({l-1,r,0});
else v.push_back({l-1,r,1});
all.push_back(l-1);
all.push_back(r);
}
sort(all.begin(),all.end());
all.erase(unique(all.begin(),all.end()),all.end());

int ans = 0;
for(int i = 0;i < all.size();i++)
{
fa[i] = i;
d[i] = 0;
}

for(auto i : v)
{
int u = search(i.l), v = search(i.r);
if(i.flag)
{
int x = find(u), y = find(v);
if(x == y)
{
if((d[u] + d[v]) % 2 == 0) break;
}
else
{
fa[x] = y;
d[x] = (d[u] + d[v] + 1) % 2;
}
}
else
{
int x = find(u), y = find(v);
if(x == y)
{
if((d[u] + d[v]) % 2 == 1) break;
}
else
{
fa[x] = y;
d[x] = (d[u] + d[v]) % 2;
}
}
ans++;
}
printf("%d\n",ans);
return 0;
}


238. Legend of Galaxy Heroes - AcWing question bank

To maintain the distance between two warships, the complexity is O ( n 2 ) O(n^2) O(n2) level.

Therefore, maintain the distance from the warship to the platoon

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;
const int MAXN = 30010;
int T;
int fa[MAXN], d[MAXN], sz[MAXN];
int find(int x)
{
if(x != fa[x])
{
int root = find(fa[x]);
d[x] += d[fa[x]];	// Complete path compression
fa[x] = root;
}
return fa[x];
}

int main()
{
for(int i = 1;i <= 30000;i++)
{
fa[i] = i;
d[i] = 0;	// Represents the distance from each set to the root node
sz[i] = 1;	// Represents the size of each collection
}
scanf("%d",&T);
while(T--)
{
char op[2];
int i, j;
scanf("%s%d%d",op,&i,&j);
if(*op == 'M')
{
int u = find(i), v = find(j);
fa[u] = v;
d[u] = sz[v];	// The distance of each vertex plus the original length is equivalent to the row head node plus the set size
sz[v] += sz[u];
}
else
{
int u = find(i), v = find(j);
if(u != v) printf("-1\n");
else if(i == j) printf("0\n");
else printf("%d\n",abs(d[i]-d[j])-1);

}
}
return 0;
}


## 2. Tree array

### Basic principles

• Quick prefix sum O ( log ⁡ n ) O(\log n) O(logn)
• Modify a number O ( log ⁡ n ) O(\log n) O(logn)

Based on binary optimization

x = 2 i k + 2 i k = 1 + 2 i k − 2 + ⋯ + 2 i 1 log ⁡ x ≥ i k ≥ i k − 1 ≥ i k − 2 ≥ ⋯ ≥ i 1 x = 2^{i_k}+2^{i_{k=1}}+2^{i_{k-2}}+\dots +2^{i_1} \qquad \qquad \log x \ge i_k \ge i_{k-1} \ge i_{k-2} \ge \dots \ge i_1 x=2ik​+2ik=1​+2ik−2​+⋯+2i1​logx≥ik​≥ik−1​≥ik−2​≥⋯≥i1​

① ( x − 2 i 1 , x ] (x-2^{i_1},x] (x−2i1​,x]

② ( x − 2 i 1 − 2 i 2 , x − 2 i 1 ] (x-2^{i_1}-2^{i_2},x-2^{i_1}] (x−2i1​−2i2​,x−2i1​]

③ ( x − 2 i 1 − 2 i 2 − 2 i 3 , x − 2 i 1 − 2 i 2 ] (x-2^{i_1}-2^{i_2}-2^{i_3},x-2^{i_1}-2^{i_2}] (x−2i1​−2i2​−2i3​,x−2i1​−2i2​]

... \dots ...

k ( x − 2 i 1 − 2 i 2 − ⋯ − 2 i k = 0 , x − 2 i 1 − 2 i 2 − ⋯ − 2 i k − 1 ] (x-2^{i_1}-2^{i_2}-\dots -2^{i_k} = 0,x-2^{i_1}-2^{i_2}-\dots -2^{i_{k-1}}] (x−2i1​−2i2​−⋯−2ik​=0,x−2i1​−2i2​−⋯−2ik−1​]

Discovery interval ( L , R ] (L,R] The interval length of (L,R] is the power corresponding to the last bit 1 of R binary l o w b i t ( R ) lowbit(R) lowbit(R)

Interval is [ R − l o w b i t ( R ) + 1 , R ] [R-lowbit(R)+1,R] [R−lowbit(R)+1,R]

remember c [ x ] c[x] c[x] is interval a [ x − l o w b i t ( x ) + 1 , x ] a[x-lowbit(x)+1,x] a[x − lowbit(x)+1,x] sum of all numbers

c i = a i + a i − 1 + ... ... + a i − l o w b i t ( i ) + 1 c_i = a_i+a_{i-1}+......+a_{i-lowbit(i)+1} ci​=ai​+ai−1​+......+ai−lowbit(i)+1​

c[1] = a[1]

c[2] = a[2]+a[1]

c[3] = a[3]

c[4] = a[4]+a[3]+a[2]+a[1]

#### Tree array query O ( log ⁡ n ) O(\log n) O(logn)

eg query a[1]+a[2] +... + a[7]

a[7] = c[7] a[6]+a[5] = c[6] a[4]+a[3]+a[2]+a[1] = c[4]

7 = ( 111 ) 2 (111)_2 (111)2​ 6 = ( 110 ) 2 (110)_2 (110)2​ 4 = ( 100 ) 2 (100)_2 (100) 2 * subtract lowbit each time

// Sum of query a[1~x]
int sum(int x)
{
int ret = 0;
while(x)
{
ret += c[x];
x -= lowbit(x);
}
return ret;
}


#### Tree array modification O ( log ⁡ n ) O(\log n) O(logn)

The parent node of X is x+lowbit(x)

Each modification will affect this node to the node under the root path

// a[i] += k
void change(int i, int k)
{
while(i <= n)
{
c[i] += k;
i += lowbit(i);
}
}


#### lowbit(i) solution

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

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

// Sum of query a[1~x]
int sum(int x)
{
int ret = 0;
while(x)
{
ret += c[x];
x -= lowbit(x);
}
return ret;
}

// a[i] += k
void change(int i, int k)
{
while(i <= n)
{
c[i] += k;
i += lowbit(i);
}
}


241. Loulan Totem - AcWing question bank

The tree array maintains the number of occurrences larger or smaller than it

Traverse each number a[i] from left to right, and use the tree array to count the number of all numbers larger than a[i] and smaller than a[i] before position i. after the statistics are completed, add a[i] to the tree array.

Traverse each number a[i] from right to left, and use the tree array to count the number of all numbers larger than a[i] and smaller than a[i] after position i. after the statistics are completed, add a[i] to the tree array.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

typedef long long ll;
const int MAXN = 2e5+50;
int a[MAXN], t[MAXN];
int small[MAXN], big[MAXN];
int n;

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

int sum(int x)
{
int res = 0;
while(x)
{
res += t[x];
x -= lowbit(x);
}
return res;
}

void add(int i, int k)
{
while(i <= n)
{
t[i] += k;
i += lowbit(i);
}
}

int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;i++) scanf("%d",&a[i]);

for(int i = 1;i <= n;i++)	// Sequential search
{
big[i] = sum(n)-sum(a[i]);	// Find a number larger than a[i]
small[i] = sum(a[i]-1);	// Find a number smaller than a[i]
add(a[i],1);
}

memset(t,0,sizeof(t));
ll resv = 0, resa = 0;

for(int i = n;i >= 1;i--)
{
resv += (ll)big[i]*(sum(n)-sum(a[i]));
resa += (ll)small[i]*sum(a[i]-1);
add(a[i],1);
}
printf("%lld %lld\n",resv,resa);

return 0;
}


### extend

#### Interval modification and single point query

• a [ L , R ] a[L,R] Each element in a[L,R] + c
• seek a [ x ] a[x] What is a[x]

Using differential array + tree array

Interval modification is equivalent to modifying two points in the difference array

Single point query is equivalent to the prefix sum of difference fraction group

242. A simple integer problem - AcWing question bank

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
const int MAXN = 100010;
int n, m;
ll a[MAXN], cf[MAXN], t[MAXN];

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

ll sum(int x)
{
ll res = 0;
while(x)
{
res += t[x];
x -= lowbit(x);
}
return res;
}

void add(int x, int p)
{
while(x <= n)
{
t[x] += p;
x += lowbit(x);
}
}

int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++) scanf("%lld",&a[i]);
for(int i = 1;i <= n;i++) {
cf[i] = a[i]-a[i-1];
add(i,cf[i]);
}
for(int i = 1;i <= m;i++)
{
char op[2];
scanf("%s",op);
if(*op == 'Q')
{
int x;
scanf("%d",&x);
printf("%lld\n",sum(x));
}
else
{
int l, r, d;
scanf("%d%d%d",&l,&r,&d);
add(l,d);
add(r+1,-d);
}

}
return 0;
}


#### Interval modification and interval summation

Interval modification: differential array a [ L , R ] + = c a[L,R] += c a[L,R]+=c is equivalent to b [ R + 1 ] − = c b [ L ] − = c b[R+1] -= c \qquad b[L] -= c b[R+1]−=cb[L]−=c

Interval summation: a [ 1 ] + a [ 2 ] + a [ 3 ] + ⋯ + a [ x ] a[1]+a[2]+a[3]+\dots+a[x] a[1]+a[2]+a[3]+⋯+a[x] = ∑ i = 1 x a [ i ] = ∑ i = 1 x ∑ j = 1 i b [ j ] \sum_{i=1}^xa[i] = \sum_{i=1}^{x} \sum_{j=1}^i b[j] ∑i=1x​a[i]=∑i=1x​∑j=1i​b[j]

Consider complement

a [ 1 ] + a [ 2 ] + a [ 3 ] + ⋯ + a [ x ] = ( x + 1 ) ∗ ( b [ 1 ] + b [ 2 ] + ⋯ + b [ x ] ) − ( 1 ∗ b [ 1 ] + 2 ∗ b [ 2 ] + 3 ∗ b [ 3 ] + ⋯ + x ∗ b [ x ] ) a[1]+a[2]+a[3]+\dots+a[x] = (x+1)*(b[1]+b[2]+\dots+b[x])-(1*b[1]+2*b[2]+3*b[3]+\dots+x*b[x]) a[1]+a[2]+a[3]+⋯+a[x]=(x+1)∗(b[1]+b[2]+⋯+b[x])−(1∗b[1]+2∗b[2]+3∗b[3]+⋯+x∗b[x])

Front is b [ i ] b[i] Prefix and of b[i], followed by i ∗ b [ i ] i*b[i] Prefix and of I * b[i]

243. A simple integer problem 2 - AcWing question bank

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
const int MAXN = 1e5+50;
int n, m;
ll a[MAXN], t[MAXN], cf[MAXN], ti[MAXN];

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

ll sum(int x)
{
ll res = 0;
while(x)
{
res += t[x];
x -= lowbit(x);
}
return res;
}

ll sumi(int x)
{
ll res = 0;
while(x)
{
res += ti[x];
x -= lowbit(x);
}
return res;
}

void add(int x, ll p)
{
while(x <= n)
{
t[x] += p;
x += lowbit(x);
}
}

void addi(int x, ll p)
{
while(x <= n)
{
ti[x] += p;
x += lowbit(x);
}
}

int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++) scanf("%lld",&a[i]);
for(int i = 1;i <= n;i++) {
cf[i] = a[i] - a[i-1];
add(i,cf[i]);
addi(i,i*cf[i]);
}
for(int i = 1;i <= m;i++)
{
char op[2];
scanf("%s",op);
if(*op == 'Q')
{
int l, r;
scanf("%d%d",&l,&r);
ll ans = ((r+1)*(sum(r))-sumi(r)) - ((l)*sum(l-1) - sumi(l-1));
printf("%lld\n",ans);
}
else
{
int l, r, d;
scanf("%d%d%d",&l,&r,&d);
add(l,d);
add(r+1,-d);
addi(l,(ll)d*l);
addi(r+1,-(ll)d*(r+1));
}
}
return 0;
}


244. Enigmatic cow AcWing question bank

Find the smallest number of k at a time

And delete it

Balance tree can be used to do.

If a tree array is used, all of them are initialized to 1. Each time, find the minimum x of sum(x)=k and subtract it by one

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
const int MAXN = 1e5+50;
int a[MAXN], t[MAXN], ans[MAXN];
int n;

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

int sum(int x)
{
int res = 0;
while(x)
{
res += t[x];
x -= lowbit(x);
}
return res;
}

void add(int i, int k)
{
while(i <= n)
{
t[i] += k;
i += lowbit(i);
}
}

int main()
{
scanf("%d",&n);
a[1] = 0;
for(int i = 2;i <= n;i++) scanf("%d",&a[i]);
for(int i = 1;i <= n;i++) add(i,1);

for(int i = n;i >= 1;i--)
{
int l = 1, r = n;
while(l < r)
{
int mid = (l+r) >> 1;
if(sum(mid) < a[i]+1) l = mid+1;
else r = mid;
}
ans[i] = l;
add(l,-1);
}
for(int i = 1;i <= n;i++) printf("%d\n",ans[i]);
return 0;
}


#### Two dimensional tree array

// a[x][y] += z;
int update(int x, int y, int z)
{
int i = x;
while(i <= n)
{
int j = y;
while(j <= m)
{
c[i][j] += z;
j += lowbit(j);
}
i += lowbit(i);
}
}

// a[1][1] + ...... + a[1][y] + a[2][1] + ...... a[2][n] + ...... +a[x][1] + ...... + a[x][y]
int sum(int x, int y)
{
int res = 0, i = x;
while(i > 0)
{
j = y;
while(j > 0)
{
res += c[i][j];
j -= lowbit(j);
}
i -= lowbit(i);
}
return res;
}


## 3. Segment tree

A complete binary tree, each node represents an interval.

section [ L , R ] [L,R] [L,R] is divided into [ L , m i d ] , [ m i d + 1 , R ] [L,mid],[mid+1,R] [L,mid],[mid+1,R] m i d = ⌊ L + R 2 ⌋ mid = \lfloor \frac{L+R}{2} \rfloor mid=⌊2L+R​⌋

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-u52lrhcn-1633831709717) (C: / users / Dell / appdata / roaming / typora / typora user images / image-20210322084255533. PNG)]

Current node x of line segment tree

Parent node ⌊ x 2 ⌋ \lfloor \frac{x}{2} \rfloor ⌊2x​⌋

Left son x < < 1 x << 1 x<<1

Right son x < < 1 ∣ 1 x << 1 | 1 x<<1∣1

Maximum range n < < 2 n << 2 n<<2

Five operations

• pushup
• pushdown
• build
• modify
• query

### node structure

struct node{
int l, r;
ll sum, lazy;	// Sum of interval [l,r]
};

int n, m;
int w[MAXN];
node tr[MAXN << 2];


### pushup

// Sum of parent nodes
void pushup(int u)
{
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}


### pushdown

void pushdown(int u)	// Pass lazy flag down
{
ll lazy = tr[u].lazy;
if(lazy)
{
tr[u << 1].lazy += lazy;
tr[u << 1 | 1].lazy += lazy;
tr[u << 1].sum += (tr[u << 1].r - tr[u << 1].l + 1) * lazy;
tr[u << 1 | 1].sum += (tr[u << 1 | 1].r - tr[u << 1 | 1].l + 1)*lazy;
tr[u].lazy = 0; 	// The lazy flag of the parent node is reset to 0
}
}


### Build a tree

//build(1,1,n) establishes a segment tree from interval 1 to n
void build(int u, int l, int r)
{
if(l == r)
{
tr[u] = {l,r,w[r]};
return;
}
else
{
tr[u] = {l,r,0};
int mid = (l+r) >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid+1, r);
pushup(u);
}
}


### inquiry

// Sum interval [l,r]
// Call (1,l,r)
ll query(int u, int l, int r)
{
if(l <= tr[u].l && tr[u].r <= r) return tr[u].sum;
else
{
pushdown(u);	// Drop lazy tag
int mid = (tr[u].l + tr[u].r) >> 1;
ll sum = 0;
if(l <= mid) sum += query(u << 1, l, r);
if(r > mid) sum += query(u << 1 | 1, l, r);
return sum;
}
}


### Single point modification

// Call (1,x,v)
// a[x] += v
void modify(int u, int x, int v)
{
if(tr[u].l == tr[u].r) tr[u].sum += 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);
}
}


### Interval modification

// a[l]~a[r] += d
void change(int u, int l, int r, int d)	// Interval modification
{
if(l <= tr[u].l && tr[u].r <= r) //If the parent node is completely overwritten, just mark the lazy flag. Don't worry about sending it down first, and then send it down when the sum needs to be used
{
tr[u].lazy += d;
tr[u].sum += (tr[u].r - tr[u].l + 1) * d;
return;
}
else
{
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if(l <= mid) change(u << 1, l, r, d);
if(r > mid) change(u << 1 | 1, l, r, d);
pushup(u);
}
}

struct node{
int l, r;
ll sum, lazy;	// Sum of interval [l,r]
};

int n, m;
int w[MAXN];
node tr[MAXN << 2];

// Sum of parent nodes
void pushup(int u)
{
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}

void pushdown(int u)	// Pass lazy flag down
{
ll lazy = tr[u].lazy;
if(lazy)
{
tr[u << 1].lazy += lazy;
tr[u << 1 | 1].lazy += lazy;
tr[u << 1].sum += (tr[u << 1].r - tr[u << 1].l + 1) * lazy;
tr[u << 1 | 1].sum += (tr[u << 1 | 1].r - tr[u << 1 | 1].l + 1)*lazy;
tr[u].lazy = 0; 	// The lazy flag of the parent node is reset to 0
}
}

//build(1,1,n) establishes a segment tree from interval 1 to n
void build(int u, int l, int r)
{
if(l == r)
{
tr[u] = {l,r,w[r]};
return;
}
else
{
tr[u] = {l,r,0};
int mid = (l+r) >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid+1, r);
pushup(u);
}
}

// Sum interval [l,r]
// Call (1,l,r)
ll query(int u, int l, int r)
{
if(l <= tr[u].l && tr[u].r <= r) return tr[u].sum;
else
{
pushdown(u);	// Drop lazy tag
int mid = (tr[u].l + tr[u].r) >> 1;
ll sum = 0;
if(l <= mid) sum += query(u << 1, l, r);
if(r > mid) sum += query(u << 1 | 1, l, r);
return sum;
}
}

// Call (1,x,v)
// a[x] += v
void modify(int u, int x, int v)
{
if(tr[u].l == tr[u].r) tr[u].sum += 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);
}
}

// a[l]~a[r] += d
void change(int u, int l, int r, int d)	// Interval modification
{
if(l <= tr[u].l && tr[u].r <= r) //If the parent node is completely overwritten, just mark the lazy flag. Don't worry about sending it down first, and then send it down when the sum needs to be used
{
tr[u].lazy += d;
tr[u].sum += (tr[u].r - tr[u].l + 1) * d;
return;
}
else
{
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if(l <= mid) change(u << 1, l, r, d);
if(r > mid) change(u << 1 | 1, l, r, d);
pushup(u);
}
}


1275. Maximum number - AcWing question bank

Maintenance interval maximum

Template question, single point modification, interval maximum.

Maximum value of interval = max (maximum value of left interval, maximum value of right interval)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
const int MAXN = 2e5+50;
struct node{
int l, r;
int v;
}tr[MAXN*4];

int m, p;

void pushup(int u)
{
tr[u].v = max(tr[u << 1].v,tr[u << 1 | 1].v);
}

void build(int u, int l, int r)
{
tr[u] = {l,r};
if(l == r) return;
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid+1, r);
}

int query(int u, int l, int r)
{
if(tr[u].l >= l && tr[u].r <= r) return tr[u].v;
else
{
int mid = tr[u].l + tr[u].r >> 1;
int v = 0;
if(l <= mid) v = query(u << 1, l, r);
if(r > mid) v = max(v,query(u << 1 | 1, l, r));
return v;
}
}

void modify(int u, int x, int v)
{
if(tr[u].l == tr[u].r) tr[u].v = 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);
}
}

int main()
{
scanf("%d%d",&m,&p);
build(1,1,m);
int n = 0, last = 0;
while(m--)
{
char op[3];
scanf("%s",op);
if(*op == 'A')
{
int t;
scanf("%d",&t);
modify(1,n+1,(t+last)%p);
n++;
}
else
{
int l;
scanf("%d",&l);
last = query(1,n-l+1,n);
printf("%d\n",last);
}
}
return 0;
}


245. Can you answer these questions - AcWing question bank

① Single point modification

② Maximum sub segment sum of query interval = max (maximum sub segment sum of left interval, maximum sub segment sum of right interval, l_max+r_max)

Save in structure, left and right end points of interval, maximum continuous sub segment sum, maximum prefix sum, maximum suffix sum, interval sum.

Output structure when querying

Pass reference

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
const int MAXN = 5e5 + 50;
int n, m;

int a[MAXN];
struct ST{
int l, r;
int lmax, rmax, tmax, sum;
}tr[MAXN * 4];

void pushup(ST &u, ST &l, ST &r)
{
u.sum = l.sum + r.sum;
u.lmax = max(l.lmax, l.sum + r.lmax);
u.rmax = max(r.rmax, r.sum + l.rmax);
u.tmax = max(max(l.tmax, r.tmax), l.rmax + r.lmax);
}

void pushup(int u)
{
pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}

void build(int u, int l, int r)
{
tr[u] = {l, r};
if(l == r)
{
tr[u].sum = tr[u].lmax = tr[u].rmax = tr[u].tmax = a[l];
return;
}
int mid = (l + r) >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}

ST query(int u, int l, int r)
{
if(tr[u].l >= l && tr[u].r <= r) {
return tr[u];
}
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 {
ST root;
auto left = query(u << 1, l, r);
auto right = query(u << 1 | 1, l, r);
pushup(root, left, right);
return root;
}

}

void change(int u, int x, int y)
{
if(tr[u].l == x && tr[u].r == x)
{
tr[u].sum = y;
tr[u].lmax = y;
tr[u].tmax = y;
tr[u].rmax = y;
return;
}
int mid = (tr[u].l + tr[u].r) >> 1;
if(x <= mid) change(u << 1, x, y);
else change(u << 1 | 1, x, y);
pushup(u);
}

int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
build(1, 1, n);
for(int i = 0; i < m; i++)
{
int op;
scanf("%d", &op);
if(op == 1)
{
int l, r;
scanf("%d%d", &l, &r);
if(l > r) swap(l, r);
auto ans = query(1, l, r);
printf("%d\n", ans.tmax);
}
else {
int x, y;
scanf("%d%d", &x, &y);
change(1, x, y);
}
}
return 0;
}


243. A simple integer problem 2 - AcWing question bank

Lazy flag (interval modification and interval query)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
const int MAXN = 1e5+50;
int n, m;
ll a[MAXN];

struct node{
int l, r;
ll sum, lazy;
}tr[MAXN * 4];

void pushup(int u)
{
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}

void pushdown(int u)
{
ll lazy = tr[u].lazy;
if(lazy)
{
tr[u << 1].lazy += lazy;
tr[u << 1 | 1].lazy += lazy;
tr[u << 1].sum += (tr[u << 1].r - tr[u << 1].l + 1) * lazy;
tr[u << 1 | 1].sum += (tr[u << 1 | 1].r - tr[u << 1 | 1].l + 1) * lazy;
tr[u].lazy = 0;
}
}

void build(int u, int l, int r)
{
if(l == r) {
tr[u] = {l, r, a[l]};
return;
}
else {
tr[u] = {l, r};
int mid = (l + r) >> 1;
build(u << 1,  l, mid);
build(u << 1 | 1, mid+1, r);
pushup(u);
}
}

void change(int u, int l, int r, int d)
{
if(l <= tr[u].l && tr[u].r <= r)
{
tr[u].lazy += d;
tr[u].sum += (tr[u].r - tr[u].l + 1) * d;
return;
}
else
{
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if(l <= mid) change(u << 1, l, r, d);
if(r > mid) change(u << 1 | 1, l, r, d);
pushup(u);
}
}

ll query(int u, int l, int r)
{
if(l <= tr[u].l && tr[u].r <= r) return tr[u].sum;
else
{
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
ll sum = 0;
if(l <= mid) sum = query(u << 1, l, r);
if(r > mid) sum += query(u << 1 | 1, l, r);
return sum;
}
}

int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++) scanf("%lld",&a[i]);
build(1,1,n);
while(m--)
{
char op[2];
scanf("%s",op);
if(*op == 'Q') {
int l, r;
scanf("%d%d",&l,&r);
ll ans = query(1,l,r);
printf("%lld\n",ans);
}
else {
int l, r, d;
scanf("%d%d%d",&l,&r,&d);
change(1,l,r,d);
}
}
return 0;
}


1277. Maintenance sequence - AcWing question bank

Maintain multiplication and addition of interval modification. Lazy tags that require addition and multiplication

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
const int MAXN = 1e5+50;
int n, p, T;
int a[MAXN];
struct node{
int l, r;
ll sum, add, mul; // mul is the lazy mark of multiplication
}tr[MAXN * 4];

void pushup(int u)
{
tr[u].sum = (tr[u << 1].sum + tr[u << 1 | 1].sum) % p;
}

void pushdown(int u)
{
ll add = tr[u].add, mul = tr[u].mul;
tr[u << 1].add = (tr[u << 1].add * mul + add) % p;
tr[u << 1 | 1].add = (tr[u << 1 | 1].add * mul + add) % p;
tr[u << 1].mul = tr[u << 1].mul * mul % p;
tr[u << 1 | 1].mul = tr[u << 1 | 1].mul * mul % p;
tr[u << 1].sum = (tr[u << 1].sum * mul + (tr[u << 1].r - tr[u << 1].l + 1) * add) % p;
tr[u << 1 | 1].sum = (tr[u << 1 | 1].sum * mul + (tr[u << 1 | 1].r - tr[u << 1 | 1].l + 1) * add) % p;
tr[u].add = 0;
tr[u].mul = 1;
}

void build(int u, int l, int r)
{
if(l == r) {
tr[u] = {l,r,a[l],0,1};
return;
}
else
{
tr[u] = {l, r, 0, 0, 1};
int mid = (l + r) >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid+1, r);
pushup(u);
}
}

void change(int u, int l, int r, int c, int a)
{
if(l <= tr[u].l && tr[u].r <= r)
{
tr[u].sum = (tr[u].sum * c + a * (tr[u].r - tr[u].l + 1)) % p;
tr[u].add = (tr[u].add * c + a) % p;
tr[u].mul = (tr[u].mul * c) % p;
}
else
{
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if(l <= mid) change(u << 1, l, r, c, a);
if(r > mid) change(u << 1 | 1, l, r, c, a);
pushup(u);
}
}

int query(int u, int l, int r)
{
if(l <= tr[u].l && tr[u].r <= r)
{
return tr[u].sum;
}
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
ll res = 0;
if(l <= mid)
{
res += query(u << 1, l, r);
res = res % p;
}
if(r > mid)
{
res += query(u << 1 | 1, l, r);
res = res % p;
}
return res;
}

int main()
{
scanf("%d%d",&n,&p);
for(int i = 1;i <= n;i++) scanf("%d",&a[i]);
build(1,1,n);
scanf("%d",&T);
while(T--)
{
int op, l, r, c;
scanf("%d",&op);
if(op == 1) {
scanf("%d%d%d",&l,&r,&c);
change(1, l, r, c, 0);
}
else if(op == 2) {
scanf("%d%d%d",&l,&r,&c);
change(1,l,r,1,c);
}
else{
scanf("%d%d",&l,&r);
int res = query(1,l,r);
printf("%d\n",res);
}
}
return 0;
}


## 4. Persistent data structure

### Persistence of Trie

Each updated version is stored to store all historical versions of the data structure.

Only record the differences between each version and the previous version.

256. Maximum XOR sum - AcWing question bank

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

typedef long long ll;
const int MAXN = 6e5+50;
int s[MAXN], tr[MAXN*25][2], idx, max_id[MAXN*25], root[MAXN];
// Root stores the root information of each version, max_id stores the largest left endpoint in the current subtree
int n, m;

// Insert operation, the ith number, bit k, p is the old version and q is the new version
void insert(int i, int k, int p, int q)
{
// If you insert the last bit
if(k < 0)
{
max_id[q] = i;
return;
}
int u = s[i] >> k & 1;
// If there are several other bits, copy directly
if(p) tr[q][!u] = tr[p][!u];
// Create a new node with the current bit
tr[q][u] = ++idx;
// Insert next
insert(i,k-1,tr[p][u], tr[q][u]);
// Finding child nodes after recursion
max_id[q] = max(max_id[tr[q][0]],max_id[tr[q][1]]);
}

int query(int root, int c, int l)
{
int p = root;
for(int i = 23;i >= 0;i--)
{
int u = c >> i & 1;
if(max_id[tr[p][!u]] >= l) p = tr[p][!u]; // If the current bit can go! u. Just go
else p = tr[p][u];
}
return c ^ s[max_id[p]];
}

int main()
{
scanf("%d%d",&n,&m);
// Insert root
max_id[0] = -1;
root[0] = ++idx;
insert(0,23,0,root[0]);

for(int i = 1;i <= n;i++)
{
int x;
scanf("%d",&x);
s[i] = s[i-1] ^ x;
root[i] = ++idx;
insert(i,23,root[i-1],root[i]);
}

while(m--)
{
char op[2];
scanf("%s",op);
if(*op == 'A')
{
int x;
scanf("%d",&x);
n++;
s[n] = s[n-1] ^ x;
root[n] = ++idx;
insert(n,23,root[n-1],root[n]);
}
else
{
int l, r, x;
scanf("%d%d%d",&l,&r,&x);
int ans = query(root[r-1],s[n]^x,l-1); // Version r-1, limit l-1
printf("%d\n",ans);
}
}

return 0;
}


## 5. RMQ (ST form)

Dynamic programming similar to multiplication

The maximum value of the quick query interval cannot be modified

f [ i , j ] f[i, j] f[i,j] indicates from i i i start, length 2 j 2^j Interval maximum of 2j +

##### initialization:

f [ i , j ] = m a x ( f [ i , j − 1 ] , f [ i + 2 j − 1 , j − 1 ] ) f[i,j] = max(f[i, j-1], f[i+2^{j-1}, j-1]) f[i,j]=max(f[i,j−1],f[i+2j−1,j−1])

##### query [ L , R ] [L,R] [L,R]:

R − L + 1 = l e n R - L + 1 = len R−L+1=len

k = ⌊ l o g 2 l e n ⌋ k = \lfloor log_2^{len} \rfloor k=⌊log2len​⌋

f [ L , R ] = m a x ( f [ L , k ] , f [ R − 2 k + 1 , k ] f[L, R] = max(f[L, k], f[R-2^k+1, k] f[L,R]=max(f[L,k],f[R−2k+1,k]​

##### Template
const int MAXN = 2e5+50, N = 18;
int n, m;
int a[MAXN], dp[MAXN][N];

void init()
{
for(int j = 0;j < N;j++)
{
for(int i = 1;i + (1 << j) - 1 <= n;i++)
{
if(j == 0) dp[i][j] = a[i];
else dp[i][j] = max(dp[i][j-1], dp[i + (1 << j - 1)][j-1]);
}
}
}

int query(int l, int r)
{
int len = r - l + 1;
int k = log(len) / log(2);
int ans = max(dp[l][k], dp[r - (1 << k) + 1][k]);
return ans;
}


### 1273. Memory of genius - AcWing question bank (template questions)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>

using namespace std;
const int MAXN = 2e5+50, N = 18;
int n, m;
int a[MAXN], dp[MAXN][N];

void init()
{
for(int j = 0;j < N;j++)
{
for(int i = 1;i + (1 << j) - 1 <= n;i++)
{
if(j == 0) dp[i][j] = a[i];
else dp[i][j] = max(dp[i][j-1], dp[i + (1 << j - 1)][j-1]);
}
}
}

int query(int l, int r)
{
int len = r - l + 1;
int k = log(len) / log(2);
int ans = max(dp[l][k], dp[r - (1 << k) + 1][k]);
return ans;
}

int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;i++) scanf("%d",&a[i]);
init();
scanf("%d",&m);
while(m--)
{
int l, r;
scanf("%d%d",&l,&r);
int ans = query(l, r);
printf("%d\n",ans);
}

return 0;
}


Tags: data structure

Posted on Sat, 09 Oct 2021 22:43:52 -0400 by \$kevan