天坑树链剖分
树链剖分要
两次dfs,划分树链,之后写个线段树进行区间操作
线段树真tm天坑
注意点:
下放懒标记的同时,把子区间的值更新
updata函数里面,更新了当前区间的话,函数结尾要更新当前区间
不要把rk填成id
可以不用写链式前向星的
别把懒标记从叶子节点下放
struct edge
{
int to;
ll va;
};
int main()
{//将使用链式前向星的写法进行树链剖分
int n = q_;
int q = q_;
int rt = q_;
ll P = q_;
vector<edge>ed((n + 10) << 1);
vector<int>head(n + 1, -1), nxt((n + 10) << 1, -1);
vector<ll>f(n + 1), d(n + 1), sz(n + 1), son(n + 1), rk(n + 1), top(n + 1), id(n + 1), tr((n + 1) << 2, 0);
vector<ll>w(n + 1, 0), lz((n + 1) << 2, 0);//懒标记
//dfs1 先找到父节点和子树大小,还有要找到重儿子,还有深度
int pocnt = 0;//树的节点数,用于链式前向星
int cnt = 0;//重构树上节点编号
auto add = [&](int x, int y)->void
{
ed[pocnt] = { y,0 };
nxt[pocnt] = head[x];
head[x] = pocnt++;
ed[pocnt] = { x,0 };
nxt[pocnt] = head[y];
head[y] = pocnt++;
};
ffp(i, 1, n) { w[i] = q_; }
ffp(i, 2, n)
{
int u = q_;
int v = q_;
add(u, v);
}
auto dfs1=[&](auto & dfs1, int now, int fa,int dep) -> void
{
sz[now] = 1;
d[now] = dep;
for (int i = head[now]; i != -1; i = nxt[i])
{
auto [to, va] = ed[i];
if (to == fa) { continue; }
dfs1(dfs1, to, now, dep + 1);
f[to] = now;
sz[now] += sz[to];
if (sz[to] > sz[son[now]])
{
son[now] = to;
}
}
};
dfs1(dfs1, rt, 0, 1);
//dfs2 ,维护dfn序,链接重链,处理出top,id,rk
auto dfs2 = [&](auto& dfs2, int now, int t)->void
{ //当前节点,重链顶端
top[now] = t;
id[now] = ++cnt;//dfn序列 每个点的新编号
rk[cnt] = now;//排名为cnt 的点是
if (!son[now])//如果是叶子节点
{
return;
}
dfs2(dfs2, son[now], t);
//优先选择重儿子来保证dfn序的连续性
for (int i = head[now]; i != -1; i = nxt[i])
{
int v = ed[i].to;
if (v != son[now] && v != f[now])
{
dfs2(dfs2, v, v);//处于下一条轻链的顶端
}
}
};
dfs2(dfs2, rt, rt);
//要建一棵线段树
auto pushlazy = [&](int now)->void//下放标记
{
lz[now * 2] +=lz[now];
lz[now * 2 + 1] += lz[now];
lz[now * 2] %= P;
lz[now * 2 + 1] %= P;
};//给标记的同时已经把值改变了,不用再改变了
auto query = [&](auto& query, int now, int l, int r, int pl, int pr)->ll
{
if (pl<=l && r<=pr)//到需要的节点了
{
return tr[now];
}
if (lz[now])
{
pushlazy(now);
tr[now * 2] += (((r + l) >> 1) - l + 1) * lz[now];
tr[now * 2 + 1] += (r - (r + l) / 2) * lz[now];
tr[now * 2] %= P;
tr[now * 2 + 1] %= P;//lazy是下放标记,在有lazy标记的时候,值已经被更新了
lz[now] = 0;
}
int med = (l + r) >> 1;
ll temp = 0;
if (pl <= med)temp+=query(query, now * 2, l, med, pl, pr);
if (pr > med)temp+=query(query, now * 2 + 1, med + 1, r, pl, pr);
return temp;
};
auto updata = [&](auto& updata, int now, int l, int r, int pl, int pr, ll va)->void
{
if (pl <= l && r <= pr)//到需要的节点了
{
lz[now] += va;
tr[now] += (r - l + 1) * va;
return;
}
if (lz[now])
{
pushlazy(now);
tr[now * 2] += (((r + l) >> 1) - l + 1) * lz[now];
tr[now * 2 + 1] += (r - (r + l) / 2) * lz[now];
tr[now * 2] %= P;
tr[now * 2 + 1] %= P;//lazy是下放标记,在有lazy标记的时候,值已经被更新了
lz[now] = 0;
}
int med = (l + r) >> 1;
if (pl <= med)updata(updata, now * 2, l, med, pl, pr, va);
if (pr > med)updata(updata, now * 2 + 1, med + 1, r, pl, pr,va);
tr[now] = tr[now * 2] + tr[now * 2 + 1];
tr[now] %= P;
};
auto bud = [&](auto bud,int now,int l,int r,int p,int va)->void
{
if (l == r)
{
tr[now] = va;
return;
}
int med = (l + r) >> 1;
if (p <= med) { bud(bud, now * 2, l, med, p, va); }
else { bud(bud, now * 2 + 1, med + 1, r, p, va); }
tr[now] = (tr[now * 2] + tr[now * 2 + 1])%MOD;
return;
};
ffp(i, 1, n)
{
bud(bud, 1, 1, n, i, w[rk[i]]);
}
//由于第二次dfs的过程中,因为顺序是先重再轻,所以每一条重链的新编号是连续的
//因为是dfs,所以每一个子树的新编号也是连续的
//所以查询两点之间的点权和,所查询的所有点,当前点到当前重链顶部的点在id编号上都是连续的区间
//于是可以考虑用线段树维护一段区间中的值,时间复杂度logn^2
auto op1 = [&](int x, int y)->ll // 操作1,询问两点之间简单路径的点权和
{
ll ans = 0;
while (top[x] != top[y])//如果两个点不在同一条链上
{
if (d[top[x]] < d[top[y]]) { swap(x, y); }//把x改为所在链链顶最深的那个点
ll res = query(query, 1, 1, n, id[top[x]], id[x]);//在线段树上查找这一段区间之和
ans += res;
ans %= P;
x = f[top[x]];
}//直到两个点处于同一条链上
if (d[x] < d[y]) { swap(x, y); }
ll res = query(query, 1, 1, n, id[y], id[x]);
ans += res;
return ans % P;
};
auto op2 = [&](int x)->ll//查询该点及子树的点权和
{
ll ans = 0;
ans = query(query, 1, 1, n, id[x], id[x] + sz[x] - 1);
return ans%P;
};
//区间修改跟区间查询一样,但是要把query修改成updata
auto op3 = [&](int x, int y,int va)->void
{
while (top[x] != top[y])
{
if (d[top[x]] < d[top[y]]) { swap(x, y); }
updata(updata,1,1,n,id[top[x]],id[x],va);
x = f[top[x]];
}
if (d[x] < d[y]) { swap(x, y); }
updata(updata, 1, 1, n, id[y], id[x], va);
};
auto op4 = [&](int x, int va)->void
{
updata(updata, 1, 1, n, id[x], id[x] + sz[x] - 1, va);
};
ffp(i, 1, q)
{
int op = q_;
if (op == 1)
{
int x = q_;
int y = q_;
int z = q_;
op3(x, y, z);
}
else if (op == 2)
{
int x = q_;
int y = q_;
cout<< op1(x, y) << endl;
}
else if (op == 3)
{
int x = q_;
int z = q_;
op4(x, z);
}
else if (op == 4)
{
int x = q_;
cout << op2(x) << endl;
}
}
return 0;
}

浙公网安备 33010602011771号