https://www.luogu.com.cn/problem/P3038
给出一棵n个节点的树,有m个操作,操作为将一条路径上的边权加一或询问某条边的权值
思路:边权转点权
过程:参考罗老师
如果把边权转为点权,就能按前面给出的“树链剖分 + 线段树”来解决。
例如下图(1),若把边权转为点权,显然只能把每条边上的边权赋给这条边下层的结点,得到图(2)。编程操作是:比较边(u, v)的两点的deep[u]、deep[v],把边权赋给更深的那个结点。
图4 图(1)边权树 (2)转化为点权树
转换为点权后,树剖的操作基本上一样。但是,区间求和和区间更新操作都有一点问题。
(1)区间求和。例如图(1)求从d到e的路径,d-b-e的长度是7 + 3 = 10;但是图(2)变成了7 + 2 + 3 = 12,多算了b点的权值。
(2)区间修改。例如图(1)中把从d到e的路径上的边d-b、b-e都减1,此时边b-a并没有被影响到;但是在图(2)中,把d、b、e三个结点的值都减了1,而b点的值是不该被减的。
观察到b = LCA(d, e),所以解决方法是不要处理LCA:
(1)区间[L, R]求和时,不计算LCA(L, R)的值;
(2)区间[L, R]更新时,不更新LCA(L, R)的值。
https://blog.csdn.net/weixin_43914593/article/details/109709506
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=1e5+1000;
typedef long long LL;
typedef pair<LL,LL>P;
struct Tree{
LL l,r,sum,tag;
}tree[maxn*4];
LL new_w[maxn];
///线段树部分
void push_up(LL p){
tree[p].sum=tree[p*2].sum+tree[p*2+1].sum;
}
void addtag(LL p,LL d){
tree[p].tag+=d;
tree[p].sum+=d*(tree[p].r-tree[p].l+1);
}
void push_down(LL p){
if(tree[p].tag!=0){
addtag(p*2,tree[p].tag);
addtag(p*2+1,tree[p].tag);
tree[p].tag=0;
}
}
void build(LL p,LL l,LL r){
tree[p].l=l;tree[p].r=r;tree[p].tag=0;tree[p].sum=0;
if(l==r){tree[p].sum=new_w[l];return;}
LL mid=(l+r)>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
push_up(p);
}
void modify(LL p,LL l,LL r,LL d){
if(l<=tree[p].l&&r>=tree[p].r){
addtag(p,d);
return;
}
push_down(p);
LL mid=(tree[p].l+tree[p].r)>>1;
if(l<=mid) modify(p*2,l,r,d);
if(r>mid) modify(p*2+1,l,r,d);
push_up(p);
}
LL query(LL p,LL l,LL r){
if(l<=tree[p].l&&r>=tree[p].r){
return tree[p].sum;
}
push_down(p);
LL mid=(tree[p].l+tree[p].r)>>1;
LL ans=0;
if(l<=mid) ans+=query(p*2,l,r);
if(r>mid) ans+=query(p*2+1,l,r);
return ans;
}
///树链剖分部分
LL siz[maxn],son[maxn],dep[maxn],fa[maxn];
LL top[maxn];
LL id[maxn],tot=0,a[maxn];
vector<P>g[maxn];///first存要到的节点,second存边权
void predfs(LL u,LL father){
siz[u]=1;dep[u]=dep[father]+1;
fa[u]=father;
for(LL i=0;i<g[u].size();i++){
LL v=g[u][i].first;
if(v==father) continue;
a[v]=g[u][i].second;///点权转边权
predfs(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]]){
son[u]=v;
}
}
}
void dfs(LL u,LL topx){
top[u]=topx;
id[u]=++tot;
new_w[tot]=a[u];
if(!son[u]) return;///边界叶子节点
dfs(son[u],topx);
for(LL i=0;i<g[u].size();i++){
LL v=g[u][i].first;
if(v==fa[u]||v==son[u]) continue;
dfs(v,v);
}
}
void modify_path(LL u,LL v,LL d){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]){
swap(u,v);
}
modify(1,id[top[u]],id[u],1);
u=fa[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
modify(1,id[u],id[v],1);
LL LCA=dep[u]<dep[v]?u:v;
modify(1,id[LCA],id[LCA],-1);
}
LL query_path(LL u,LL v){
LL ans=0;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]){
swap(u,v);
}
ans+=query(1,id[top[u]],id[u]);
u=fa[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
ans+=query(1,id[u],id[v]);
LL LCA=dep[u]<dep[v]?u:v;
ans-=query(1,id[LCA],id[LCA]);
return ans;
}
int main(void)
{
cin.tie(0);std::ios::sync_with_stdio(false);
LL n,m;cin>>n>>m;
for(LL i=1;i<n;i++){
LL u,v;cin>>u>>v;
g[u].push_back({v,0});
g[v].push_back({u,0});
}
predfs(1,0);
dfs(1,1);
build(1,1,n);
while(m--){
char op;LL l,r;
cin>>op>>l>>r;
if(op=='P'){
modify_path(l,r,1);
}
if(op=='Q'){
cout<<query_path(l,r)<<endl;
}
}
return 0;
}