题解 P4315 【月下“毛景树”】
VenusM1nT
2019-01-07 10:58:59
树链剖分。俗话说得好,$\text{OI}$ 毒瘤千千万,树剖 $\text{DP}$ 各一半。(纯属瞎扯,很多数据结构也很毒瘤),这道题很明显的是一道树链剖分的题,但因为操作的对象是**边权**,而树链剖分针对的应该是**点权**,所以我们要将边权转为点权来做。
此时很明显,我们会想到**将一条边的边权转移到它深度更深的端点**上,然后进行处理,但此时会有一个问题,也就是在查询路径 $u-v$ 时,$u$ 和 $v$ 的 $lca$ 的权值是它上一条边的权值,并不属于 $u-v$ 这条路径,所以我们在退出 $top[u]\not= top[v]$ 这层循环,进行最后一次操作时应将 $u$ 的 $\text{DFS}$ 序加上 $1$ ,这样就可以忽略 $lca$ 这个点了。
具体原因表述可能不太清楚:在退出循环之后,两个点的位置在同一条重链上,显然,这条重链的 $top$ 就是它们原来的 $lca$,而要忽略 $lca$ ,就是要到达比它深度 $+1$ 的点,这个点的 $\text{DFS}$ 序也显然是比 $lca$ 的 $\text{DFS}$ 序多 $1$。
最后讲一下线段树的注意事项,这题有一个比较少见的操作:区间覆盖,我们多设置一个 $\text{lazytag}$,命名为 $cov$,记录区间覆盖情况,然后在 $\text{pushdown}$ 时先处理这个 $cov$,处理完后不能重新赋值为 $0$ ,而是要赋值为 $-1$,为什么呢?因为区间覆盖的值可能为 $0$,但不可能为负数(毕竟不可能有 $-1$ 个毛毛果),初值也要赋成 $-1$。
```cpp
#include<bits/stdc++.h>
#define MAXN 100005
using namespace std;
int cnt,fst[MAXN],nxt[MAXN<<1],to[MAXN<<1],w[MAXN<<1],fr[MAXN<<1];
int n,a[MAXN],t[MAXN<<2],tag[MAXN<<2],cov[MAXN<<2];
int siz[MAXN],son[MAXN],dfn[MAXN],Index,top[MAXN],rk[MAXN],dep[MAXN],faz[MAXN];
string s;
void AddEdge(int u,int v,int c)
{
to[++cnt]=v;
nxt[cnt]=fst[u];
fst[u]=cnt;
w[cnt]=c;
fr[cnt]=u;
}
void Dfs1(int u)
{
siz[u]=1;
son[u]=0;
for(int i=fst[u];i;i=nxt[i])
{
int v=to[i];
if(v==faz[u]) continue;
faz[v]=u;
dep[v]=dep[u]+1;
rk[v]=w[i];
Dfs1(v);
siz[u]+=siz[v];
if(siz[son[u]]<siz[v]) son[u]=v;
}
}
void Dfs2(int u,int rt)
{
dfn[u]=++Index;
top[u]=rt;
a[Index]=rk[u];
if(son[u]) Dfs2(son[u],rt);
for(int i=fst[u];i;i=nxt[i])
{
int v=to[i];
if(v==faz[u] || v==son[u]) continue;
Dfs2(v,v);
}
}
void PushUp(int rt)
{
t[rt]=max(t[rt<<1],t[rt<<1|1]);
}
void PushDown(int rt)
{
if(~cov[rt])
{
cov[rt<<1]=cov[rt<<1|1]=cov[rt];
t[rt<<1]=t[rt<<1|1]=cov[rt];
tag[rt<<1]=tag[rt<<1|1]=0;
cov[rt]=-1;
}
tag[rt<<1]+=tag[rt];
tag[rt<<1|1]+=tag[rt];
t[rt<<1]+=tag[rt];
t[rt<<1|1]+=tag[rt];
tag[rt]=0;
}
void BuildSegmentTree(int rt,int l,int r)
{
cov[rt]=-1;
if(l==r)
{
t[rt]=a[l];
return;
}
int mid=l+r>>1;
BuildSegmentTree(rt<<1,l,mid);
BuildSegmentTree(rt<<1|1,mid+1,r);
PushUp(rt);
}
void ModifyCover(int rt,int l,int r,int tl,int tr,int val)
{
if(tl<=l && r<=tr)
{
t[rt]=cov[rt]=val;
tag[rt]=0;
return;
}
PushDown(rt);
int mid=l+r>>1;
if(tl<=mid) ModifyCover(rt<<1,l,mid,tl,tr,val);
if(tr>mid) ModifyCover(rt<<1|1,mid+1,r,tl,tr,val);
PushUp(rt);
}
void ModifyAdd(int rt,int l,int r,int tl,int tr,int val)
{
if(tl<=l && r<=tr)
{
t[rt]+=val;
tag[rt]+=val;
return;
}
PushDown(rt);
int mid=l+r>>1;
if(tl<=mid) ModifyAdd(rt<<1,l,mid,tl,tr,val);
if(tr>mid) ModifyAdd(rt<<1|1,mid+1,r,tl,tr,val);
PushUp(rt);
}
int Query(int rt,int l,int r,int tl,int tr)
{
if(tl<=l && r<=tr) return t[rt];
PushDown(rt);
int mid=l+r>>1,res=0;
if(tl<=mid) res=max(res,Query(rt<<1,l,mid,tl,tr));
if(tr>mid) res=max(res,Query(rt<<1|1,mid+1,r,tl,tr));
return res;
}
void ModifyCoverOnTree(int u,int v,int val)
{
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]]) swap(u,v);
ModifyCover(1,1,n,dfn[top[u]],dfn[u],val);
u=faz[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
ModifyCover(1,1,n,dfn[u]+1,dfn[v],val);
}
void ModifyAddOnTree(int u,int v,int val)
{
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]]) swap(u,v);
ModifyAdd(1,1,n,dfn[top[u]],dfn[u],val);
u=faz[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
ModifyAdd(1,1,n,dfn[u]+1,dfn[v],val);
}
int QueryMaxnOnTree(int u,int v)
{
int res=0;
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]]) swap(u,v);
res=max(res,Query(1,1,n,dfn[top[u]],dfn[u]));
u=faz[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
res=max(res,Query(1,1,n,dfn[u]+1,dfn[v]));
return res;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int x,y,z;
scanf("%d %d %d",&x,&y,&z);
AddEdge(x,y,z);
AddEdge(y,x,z);
}
Dfs1(1);
Dfs2(1,1);
BuildSegmentTree(1,1,n);
while(1)
{
int x,y,z;
cin>>s;
if(s=="Stop") break;
else
{
scanf("%d %d",&x,&y);
if(s=="Change")
{
x<<=1;
int u=fr[x],v=to[x];
if(dep[u]>dep[v]) swap(u,v);
ModifyCoverOnTree(u,v,y);
}
else if(s=="Cover")
{
scanf("%d",&z);
ModifyCoverOnTree(x,y,z);
}
else if(s=="Add")
{
scanf("%d",&z);
ModifyAddOnTree(x,y,z);
}
else if(s=="Max") printf("%d\n",QueryMaxnOnTree(x,y));
}
}
return 0;
}
```