constint M =1e5+10;int fa[M];voidinit(int n){for(int i =0; i <= n; i++) fa[i]= i;}intfindset(int x){return x == fa[x]? x : fa[x]=findset(fa[x]);}voidunite(int a,int b){
fa[findset(a)]=findset(b);}
网络赛版
classUF{public:int n;// 当前连通分量数目int cnt;
vector<int> size;
vector<int> parent;UF(int _n):n(_n +1),cnt(_n +1),size(_n +1,1),parent(_n +1){int i =0;for(auto&x : parent) x = i++;}intfindset(int x){return parent[x]== x ? x : parent[x]=findset(parent[x]);}boolunite(int x,int y){
x =findset(x);
y =findset(y);if(x == y){returnfalse;}if(size[x]< size[y]){swap(x, y);}
parent[y]= x;
size[x]+= size[y];--cnt;returntrue;}boolconn(int x,int y){
x =findset(x);
y =findset(y);return x == y;}};
2. 树状数组
单点修改与区间查询
// 修改复杂度与查询复杂度O(logn)// 修改复杂度与查询复杂度O(logn)template<classT>classfenwich{#definelowbit(x)((x)&(-x))
vector<T> v;int len;
T get_pre(int i){
T res =0;for(; i >0; i -=lowbit(i)) res += v[i];return res;}public:fenwich(int len):v(len +1,0),len(len){}voidresize(int n, T x =0){ v.resize(n +1, x), len = n;}voidmodify(int i, T val){// 单点修改,用此函数建树for(; i <= len; i +=lowbit(i)) v[i]+= val;}
T get(int l,int r){// 区间查询if(l > r)return0;returnget_pre(r)-get_pre(l -1);}#undeflowbit};
区间修改,区间查询
设
d
[
i
]
=
a
[
i
]
−
a
[
i
−
1
]
d[i] = a[i] - a[i - 1]
d[i]=a[i]−a[i−1]
则
a
[
x
]
=
∑
i
=
1
x
d
i
a[ x ] = \sum_{i = 1}^{x}{d_i}
a[x]=∑i=1xdi
设
s
u
m
[
x
]
=
∑
i
=
1
x
a
i
sum[ x ] = \sum_{i = 1}^{x}{a_i}
sum[x]=∑i=1xai
即
s
u
m
[
x
]
=
d
[
1
]
+
d
[
1
]
+
d
[
2
]
+
d
[
1
]
+
d
[
2
]
+
d
[
3
]
+
…
…
+
d
[
1
]
+
…
…
+
d
[
n
]
sum[ x ] = d[1] + d[1] + d[2] + d[1] + d[2] + d[3] + …… + d[1] + ……+ d[n]
sum[x]=d[1]+d[1]+d[2]+d[1]+d[2]+d[3]+……+d[1]+……+d[n]
化简得
s
u
m
[
x
]
=
∑
i
=
1
x
d
i
×
(
n
−
i
+
1
)
sum[ x ] = \sum_{i = 1}^{x}{d_i \times (n - i + 1)}
sum[x]=∑i=1xdi×(n−i+1)
得
s
u
m
[
x
]
=
(
n
+
1
)
×
∑
i
=
1
x
d
i
−
∑
i
=
1
x
i
×
d
i
sum[ x ] = (n + 1) \times \sum_{i = 1}^{x}{d_i} - \sum_{i = 1}^{x}{i \times d_i}
sum[x]=(n+1)×∑i=1xdi−∑i=1xi×di
固开两个树状数组,一个维护差分数组
d
i
d_i
di,一个维护
i
×
d
i
i \times d_i
i×di
template<classT>classFenwich{#definelowbit(x)((x)&(-x))
vector<T> d, id;int len;voidupdate(int indx, T val, vector<T>& vt){for(; indx <= len; indx +=lowbit(indx)) vt[indx]+= val;}
T get_pre(int indx, vector<T>& vt){
T res =0;for(; indx >0; indx -=lowbit(indx)) res += vt[indx];return res;}public:Fenwich(int n):d(n +1,0),id(n +1,0),len(n){}voidresize(int n, T x =0){
d.resize(n +1,0), id.resize(n +1,0);
len = n;}voidmodify(int l,int r, T val){// 区间修改,单点修改和建树也调用此函数update(l, val, d),update(r +1,-val, d);update(l, val * l, id),update(r +1,(-val)*(r +1), id);}
T get(int l,int r){
T res =get_pre(r, d)*(r +1)-get_pre(r, id);
res -=get_pre(l -1, d)* l -get_pre(l -1, id);return res;}#undeflowbit};
constint M =1e5+5;int st[M][30], lg[M];// st表预处理, 注意下标从1开始到n结束voidinit(int*a,int n){
lg[0]=-1;for(int i =1; i <= n;++i) lg[i]= lg[i >>1]+1, st[i][0]= a[i];for(int j =1; j <= lg[n];++j){int k =1<<(j -1);for(int i =1; i + k -1<= n;++i){
st[i][j]=max(st[i][j -1], st[i + k][j -1]);}}}// 询问// 尽可能让l + 2^(len) - 1接近rintget(int l,int r){int x = lg[r - l +1];returnmax(st[l][x], st[r -(1<< x)+1][x]);}
5. 分块
constint N =1e5+5, M =500;#definelllonglong
ll a[N];int belong[N];structblocks{int l, r;
ll lazy;blocks():lazy(0){}}b[M];// 以下函数是基本不变的voidbuild(int n){int siz =sqrt(n), cnt = n / siz;if(n % siz)++cnt;for(int i =1; i <= cnt;++i){
b[i].l =(i -1)* siz +1;
b[i].r = i * siz;}
b[cnt].r = n;for(int i =1; i <= n;++i) belong[i]=(i -1)/ siz +1;}
6. 莫队
#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>usingnamespace std;constint M =1e5+10;int n, m, block, arr[M], pos[M], ans[M], res;structMO{int l, r, k;MO(int l =0,int r =0,int k =0):l(l),r(r),k(k){}}q[M];boolcmp(MO a, MO b){if(pos[a.l]^ pos[b.l]){//不在同一个块return pos[a.l]< pos[b.l];}if(pos[a.l]&1)return a.r < b.r;return b.r < a.r;}voidadd(int x){}voiddel(int x){}voidsolve(){int l =1, r =0;for(int i =0; i < m; i++){while(l > q[i].l)add(--l);while(r < q[i].r)add(++r);while(l < q[i].l)del(l++);while(r > q[i].r)del(r--);
ans[q[i].k]= res;//res根据题目意思来}}voidinit(){scanf("%d %d",&n,&m);
block =sqrt(n);for(int i =1; i <= n; i++){scanf("%d", arr + i);
pos[i]= i / block;}for(int i =0; i < m; i++){int l, r;scanf("%d %d",&l,&r);
q[i]=MO(l, r, i);}sort(q, q + m, cmp);}intmain(){init();solve();return0;}
7. 平衡树
fhq treap
// 洛谷板子题#include<cstdio>#include<cstring>#include<algorithm>#include<random>#include<cctype>inlinelonglongIO(){}usingnamespace std;constint N =4e5+10;
mt19937 rnd(233);structtreap{int val, l, r, size, key;}fhq[N];int root, cnt;inlinevoidupdate(int now){
fhq[now].size = fhq[fhq[now].l].size + fhq[fhq[now].r].size +1;}intnew_node(int val){
fhq[++cnt]={.val = val,.l =0,.r =0,.size =1,.key =rnd()};return cnt;}voidsplit(int now,int val,int&x,int&y){if(!now){ x = y =0;return;}if(fhq[now].val <= val) x = now,split(fhq[now].r, val, fhq[now].r, y);else y = now,split(fhq[now].l, val, x, fhq[now].l);update(now);}intmerge(int x,int y){if(!x ||!y)return x | y;// 大根堆if(fhq[x].key > fhq[y].key){//右下角
fhq[x].r =merge(fhq[x].r, y),update(x);return x;}// 左下角
fhq[y].l =merge(x, fhq[y].l),update(y);return y;}// 插入inlinevoidinsert(int val){int x, y;split(root, val, x, y);
root =merge(merge(x,new_node(val)), y);}// 按值删除inlinevoiddel(int val){int x, y, z;split(root, val, x, z);split(x, val -1, x, y);
y =merge(fhq[y].l, fhq[y].r);
root =merge(merge(x, y), z);}// 按值获取排名inlineintgetrank(int val){int x, y, ans;split(root, val -1, x, y);
ans = fhq[x].size +1;
root =merge(x, y);return ans;}// 按排名获取值inlineintgetval(int rank){int now = root;while(now){if(fhq[fhq[now].l].size +1== rank)break;elseif(fhq[fhq[now].l].size >= rank) now = fhq[now].l;else rank -= fhq[fhq[now].l].size +1, now = fhq[now].r;}return fhq[now].val;}// 求前驱,即严格比val小的最大值inlineintpre(int val){int x, y;split(root, val -1, x, y);int now = x;while(fhq[now].r) now = fhq[now].r;
root =merge(x, y);return fhq[now].val;}// 求后继,即严格比val大的最小值inlineintnxt(int val){int x, y;split(root, val, x, y);int now = y;while(fhq[now].l) now = fhq[now].l;
root =merge(x, y);return fhq[now].val;}intmain(){int t =IO();while(t--){int q =IO(), val =IO();if(q ==1)insert(val);elseif(q ==2)del(val);elseif(q ==3)printf("%d\n",getrank(val));elseif(q ==4)printf("%d\n",getval(val));elseif(q ==5)printf("%d\n",pre(val));elseprintf("%d\n",nxt(val));}return0;}
#include<cstdio>#include<cstring>#include<algorithm>#include<vector>#include<cctype>inlinelonglongIO(){}// 快读略usingnamespace std;/*************************************离散化********************************************/// vt存放可用于查询原本的数(用离散化值),打表后用于查询离散化表(用下标)
vector<int> vt;inlineintget_id(constint&x){returnlower_bound(vt.begin(), vt.end(), x)- vt.begin()+1;}inlinevoiderase_vt(){sort(vt.begin(), vt.end());
vt.erase(unique(vt.begin(), vt.end()), vt.end());}// 打表, 注意,原数组下标要从1开始,返回离散化后的表大小inlineintid_table(int n,int*a, vector<int>&res){
res.emplace_back(0);for(int i =1; i <= n;++i) res.emplace_back(get_id(a[i]));return vt.size();}/*************************************主席树********************************************/constint N =2e5+5;structnodes{int l, r, sum;nodes():sum(0){}}hjt[N <<5];int root[N], cnt;// 记录每个根结点的内存池编号, 内存池intbuild(int l,int r){int now =++cnt;// 内存申请if(l < r){int mid =(l + r)>>1;
hjt[now].l =build(l, mid);
hjt[now].r =build(mid +1, r);}return now;}// 插入新节点的操作intupdate(int pre,int l,int r,int x){int now =++cnt;// 内存申请
hjt[now]= hjt[pre],++hjt[now].sum;// 继承if(l < r){// 寻找拼接点int mid =(l + r)>>1;if(x <= mid) hjt[now].l =update(hjt[now].l, l, mid, x);// 如果x在左边,则让当前新节点的左孩子接继承后的左孩子else hjt[now].r =update(hjt[now].r, mid +1, r, x);// 否则同理}return now;}// 返回第qr版本的主席树 - 第ql版本的主席树, 注意返回的是离散化后的值intget(int ql,int qr,int l,int r,int k){if(l == r)return l;int mid =(l + r)>>1;int dif = hjt[hjt[qr].l].sum - hjt[hjt[ql].l].sum;if(k <= dif)returnget(hjt[ql].l, hjt[qr].l, l, mid, k);// 左孩子上returnget(hjt[ql].r, hjt[qr].r, mid +1, r, k - dif);// 右孩子上}/*************************************主函数********************************************/int a[N];intmain(){int n =IO(), m =IO();for(int i =1; i <= n;++i) a[i]=IO(), vt.emplace_back(a[i]);erase_vt();
vector<int> id;int siz =id_table(n, a, id);
root[0]=build(1, siz);for(int i =1; i <= n;++i) root[i]=update(root[i -1],1, siz, id[i]);while(m--){int l =IO(), r =IO(), k =IO();printf("%d\n", vt[get(root[l -1], root[r],1, siz, k)-1]);}return0;}
10. LCA
// 洛谷板子题// 注意,尽量让结点编号从1开始#include<cstdio>#include<algorithm>#include<cstring>#include<cctype>#definelllonglonginlinelonglongIO(){}// 快读略usingnamespace std;constint maxn =5e5+5, maxm =5e5+5;constint INF =0x3f3f3f3f;int head[maxn], cnt;structedges{int to, next;voidadd(int t,int n){
to = t, next = n;}}edge[maxm <<1];//无向图则需要乘2inlinevoidadd(int u,int v){
edge[++cnt].add(v, head[u]);
head[u]= cnt;}int fa[maxn][35], dep[maxn], lg[maxn];/* 另一种写法
void dfs(int u, int f) {
deep[u] = deep[f] + 1;
fa[u][0] = f;
for (int i = 1; (1 << i) <= deep[u]; ++i) fa[u][i] = fa[fa[u][i - 1]][i - 1];
for (int& v : mp[u]) {
if (v ^ f) dfs(v, u);
}
}
int lca(int a, int b) {
if (deep[a] < deep[b]) swap(a, b);
for (int i = 18; ~i; --i) if (deep[fa[a][i]] >= deep[b]) a = fa[a][i];
if (a == b) return a;
for (int i = 20; ~i; --i) {
if (fa[a][i] != fa[b][i]) a = fa[a][i], b = fa[b][i];
}
return fa[a][0];
}
*/voiddfs(int u,int f){
fa[u][0]= f;
dep[u]= dep[f]+1;for(int i =1; i <= lg[dep[u]];++i) fa[u][i]= fa[fa[u][i -1]][i -1];for(int i = head[u];~i; i = edge[i].next){int v = edge[i].to;if(v ^ f)dfs(v, u);}}voidinit(int root,int n){// 通过检查代码,反向51行dep[root] = -1没意义,具体细节以后填这个坑
dep[root]= lg[0]=-1;memset(head,-1,sizeof head); cnt =0;for(int i =1; i <= n;++i) lg[i]= lg[i >>1]+1;}intlca(int a,int b){if(dep[a]< dep[b])swap(a, b);while(dep[a]> dep[b]) a = fa[a][lg[dep[a]- dep[b]]];if(a == b)return a;for(int i = lg[dep[a]];~i;--i){if(fa[a][i]!= fa[b][i]) a = fa[a][i], b = fa[b][i];}return fa[a][0];}intmain(){int n =IO(), m =IO(), root =IO();init(root, n);for(int i =1; i < n;++i){int u =IO(), v =IO();add(u, v),add(v, u);}dfs(root,0);while(m--){int a =IO(), b =IO();printf("%d\n",lca(a, b));}return0;}
11. 树链剖分
// 洛谷板子题#include<cstdio>#include<cstring>#include<algorithm>#include<vector>#include<iostream>#include<cmath>#include<bitset>usingnamespace std;#definelllonglongconstint N =1e5+5, M =2e5+5;constint maxn =1e5+5, maxm =2e5+5;constint INF =0x3f3f3f3f;int head[maxn], cnt;//初始化voidinit(){memset(head,-1,sizeof head); cnt =-1;}structedges{int to, next;voidadd(int t,int n){
to = t, next = n;}}edge[maxm <<1];//无向图则需要乘2inlinevoidadd(int u,int v){
edge[++cnt].add(v, head[u]);
head[u]= cnt;}/*******************************树链剖分**********************************/int fa[N], dep[N], siz[N], son[N];voiddfs1(int u,int f){
fa[u]= f, siz[u]=1;
dep[u]= dep[f]+1;for(int i = head[u];~i; i = edge[i].next){int v = edge[i].to;if(v == f)continue;dfs1(v, u);
siz[u]+= siz[v];if(siz[v]> siz[son[u]]) son[u]= v;// 找重儿子}}int v[N];// 点上的权值int tim, dfn[N], top[N], w[N];// w的下标是时间戳,对应的是相应时间戳上的点的点权voiddfs2(int u,int t){
dfn[u]=++tim, top[u]= t;
w[tim]= v[u];if(!son[u])return;dfs2(son[u], t);for(int i = head[u];~i; i = edge[i].next){int v = edge[i].to;if(v == fa[u]|| v == son[u])continue;dfs2(v, v);}}/*******************************线段树******************************/inlineintls(constint& x){return x <<1;}inlineintrs(constint& x){return x <<1|1;}
ll seg[N <<2], lazy[N <<2], p;int n, m;inline ll op(const ll& a,const ll& b){// seg[x] = max(seg[ls(x)], seg[rs(x)]);return(a + b)% p;}inlinevoidpush_down(constint& l,constint& r,constint& node){if(!lazy[node])return;
lazy[ls(node)]+= lazy[node], lazy[rs(node)]+= lazy[node];
lazy[ls(node)]%= p, lazy[rs(node)]%= p;int mid =(l + r)>>1;
seg[ls(node)]=(lazy[node]*(mid - l +1)+ seg[ls(node)])% p;
seg[rs(node)]=(lazy[node]*(r - mid)+ seg[rs(node)])% p;
lazy[node]=0;}voidbuild(int l,int r,int node =1){if(l == r){
seg[node]= w[l];return;}int mid =(l + r)>>1;build(l, mid,ls(node)),build(mid +1, r,rs(node));
seg[node]=op(seg[ls(node)], seg[rs(node)]);}voidupdate(int ql,int qr, ll x,int l =1,int r = n,int node =1){if(ql <= l && r <= qr){
lazy[node]=(lazy[node]+ x)% p;
seg[node]=(seg[node]+(r - l +1)* x)% p;return;}push_down(l, r, node);int mid =(l + r)>>1;if(ql <= mid)update(ql, qr, x, l, mid,ls(node));if(qr > mid)update(ql, qr, x, mid +1, r,rs(node));
seg[node]=op(seg[ls(node)], seg[rs(node)]);}intget(int ql,int qr,int l =1,int r = n,int node =1){if(ql <= l && r <= qr)return seg[node];push_down(l, r, node);int mid =(l + r)>>1, res =0;if(ql <= mid) res =get(ql, qr, l, mid,ls(node));if(qr > mid) res =op(res,get(ql, qr, mid +1, r,rs(node)));return res;}/********************************树上操作**********************************/voidupdate_chain(int x,int y, ll z){// 将树从 x 到 y 结点最短路径上所有节点的值都加上 z。while(top[x]!= top[y]){if(dep[top[x]]< dep[top[y]])swap(x, y);update(dfn[top[x]], dfn[x], z);
x = fa[top[x]];}if(dep[x]> dep[y])swap(x, y);update(dfn[x], dfn[y], z);}
ll get_chain(int x,int y){//求树从 x 到 y 结点最短路径上所有节点的值之和。int res =0;while(top[x]!= top[y]){if(dep[top[x]]< dep[top[y]])swap(x, y);
res =op(res,get(dfn[top[x]], dfn[x]));
x = fa[top[x]];}if(dep[x]> dep[y])swap(x, y);returnop(res,get(dfn[x], dfn[y]));}voidupdate_son(int x, ll z){// 将以 x 为根节点的子树内所有节点值都加上 zupdate(dfn[x], dfn[x]+ siz[x]-1, z);}
ll get_son(int x){// 求以 x 为根节点的子树内所有节点值之和returnget(dfn[x], dfn[x]+ siz[x]-1);}/********************************主函数************************************/intmain(){
std::ios::sync_with_stdio(false);
cout.tie(0), cin.tie(0);init();int root;
cin >> n >> m >> root >> p;for(int i =1; i <= n;++i) cin >> v[i];for(int i =1; i < n;++i){int u, v;
cin >> u >> v;add(u, v),add(v, u);}dfs1(root, root),dfs2(root, root);build(1, n);while(m--){int q, x, y, z;
cin >> q >> x;if(q ==1){
cin >> y >> z;update_chain(x, y, z % p);}elseif(q ==2){
cin >> y;
cout <<get_chain(x, y)<< endl;}elseif(q ==3){
cin >> z;update_son(x, z);}else{
cout <<get_son(x)<< endl;}}return0;}