题意:给一棵带边权的树,每个点开始时为白色,维护两种操作:
1.改变一个点的颜色(白变黑,黑变白)
2.询问最远的两个白点之间的距离
链分治的本质其实就是树链剖分。它们的区别是树剖维护路径询问,链分治维护全局路径。
动态点分治需要重新建一棵树,而链分治分出来的是链,本身就可以用数据结构维护。所以链分治本身就可以资瓷修改。
对于每一条链单独用数据结构维护与当前链有交集的路径信息。
对于本题,先树链剖分
记 D i , D i ′ D_i,D_i' Di,Di′为节点 i i i沿虚边往下走到某个白点的最长和次长长度。如果不存在,记为 − I N F -INF −INF
用线段树维护一条链。对于一条链上的一个区间 [ L , R ] [L,R] [L,R]
记
l m a x = m a x { d i s t ( L , i ) + D i } lmax=max\{dist(L,i)+D_i\} lmax=max{dist(L,i)+Di}
r m a x = m a x { D i + d i s t ( i , R ) } rmax=max\{D_i+dist(i,R)\} rmax=max{Di+dist(i,R)}
即: l m a x lmax lmax表示 L L L沿这条链往下走(可以不走),在 [ L , R ] [L,R] [L,R]中的某个点拐出去,往下走(可以不走)到某个白点经过的最长长度。注意没有要求起点是白色。 r m a x rmax rmax同理。
再记录 a n s ans ans表示有交集的最长长度
然后用类似最大子段和的方式合并。
对于一个线段树节点 p p p,左右儿子 l c , r c lc,rc lc,rc
l m a x [ p ] = m a x { l m a x [ l c ] , d i s t ( L , m i d + 1 ) + l m a x [ r c ] } lmax[p]=max\{lmax[lc],dist(L,mid+1)+lmax[rc]\} lmax[p]=max{lmax[lc],dist(L,mid+1)+lmax[rc]}
r m a x [ p ] = m a x { r m a x [ l c ] + d i s t ( m i d , R ) , r m a x [ r c ] } rmax[p]=max\{rmax[lc]+dist(mid,R),rmax[rc]\} rmax[p]=max{rmax[lc]+dist(mid,R),rmax[rc]}
a n s [ p ] = m a x { a n s [ l c ] , a n s [ r c ] , r m a x [ l c ] + d i s t ( m i d , m i d + 1 ) + l m a x [ r c ] } ans[p]=max\{ans[lc],ans[rc],rmax[lc]+dist(mid,mid+1)+lmax[rc]\} ans[p]=max{ans[lc],ans[rc],rmax[lc]+dist(mid,mid+1)+lmax[rc]}
边界:当 L = R L=R L=R,设当前点为 i i i
若 i i i是白点
l m a x = r m a x = m a x { D i , 0 } lmax=rmax=max\{D_i,0\} lmax=rmax=max{Di,0}
a n s = m a x { 0 , D i , D i + D i ′ } ans=max\{0,D_i,D_i+D_i'\} ans=max{0,Di,Di+Di′}
否则
l m a x = r m a x = D i lmax=rmax=D_i lmax=rmax=Di
a n s = D i + D i ′ ans=D_i+D_i' ans=Di+Di′
最后需要维护 D i , D i ′ D_i,D_i' Di,Di′
当 i i i是白点, j j j为 i i i的轻儿子
D i = m a x { 0 , w ( i , j ) + l m a x [ j ] } D_i=max\{0,w(i,j)+lmax[j]\} Di=max{0,w(i,j)+lmax[j]}
当 i i i是黑点
D i = w ( i , j ) + l m a x [ j ] D_i=w(i,j)+lmax[j] Di=w(i,j)+lmax[j]
然后用个堆之类的瞎维护即可
然而写起来十分精神污染
有一个常用的转换方式
红色边权值为
0
0
0
这样这棵树变成了二叉树,且两两之间的距离不变。也就是说和原来的树是等效的。
有什么用呢?
这样一个节点最多有一个重儿子一个轻儿子,
D
i
′
D_i'
Di′永远为
−
I
N
F
-INF
−INF,
D
i
D_i
Di可以直接计算,大大降低编程复杂度。当然好不好调是另外一回事
最后答案用 m u l t i s e t multiset multiset暴艹即可
复杂度 O ( n l o g n 2 ) O(nlog_n^2) O(nlogn2)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <set>
#define MAXN 200005
#define INF 0x3f3f3f3f
using namespace std;
inline int read()
{
int ans=0,f=1;
char c=getchar();
while (c<'0'||c>'9'){if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
return f*ans;
}
inline char gal()
{
char c=getchar();
while (c<'A'||c>'Z') c=getchar();
return c;
}
int n;
struct edge
{
int u,v,w;
}e[MAXN];
int head[MAXN],nxt[MAXN],cnt;
void addnode(int u,int v,int w)
{
e[++cnt]=(edge){u,v,w};
nxt[cnt]=head[u];
head[u]=cnt;
}
int son[MAXN][2];
int fa[MAXN],dep[MAXN],cost[MAXN],col[MAXN];
void dfs(int u)
{
int las=u;
for (int i=head[u];i;i=nxt[i])
if (!dep[e[i].v])
{
fa[fa[e[i].v]=++n]=las;
dep[e[i].v]=dep[u]+1;cost[e[i].v]=e[i].w;
dfs(son[las=son[las][las!=u]=n][0]=e[i].v);
}
}
int siz[MAXN],dis[MAXN],tp[MAXN],bt[MAXN];
int dfn[MAXN],pos[MAXN],tim;
void Dfs(int u)
{
if (!u) return;
siz[u]=1;
Dfs(son[u][0]),Dfs(son[u][1]);
siz[u]+=siz[son[u][0]]+siz[son[u][1]];
if (siz[son[u][0]]<siz[son[u][1]]) swap(son[u][0],son[u][1]);
}
multiset<int> re;
int rt[MAXN],tot;
int ch[MAXN<<2][2],lmax[MAXN<<2],rmax[MAXN<<2],ans[MAXN<<2];
inline int d(const int& x)
{
if (!son[x][1]) return col[x]? 0:-INF;
if (col[x]) return max(cost[son[x][1]]+lmax[rt[son[x][1]]],0);
return cost[son[x][1]]+lmax[rt[son[x][1]]];
}
#define lc ch[p][0]
#define rc ch[p][1]
inline void update(const int& p,const int& l,const int& r)
{
int mid=(l+r)>>1;
lmax[p]=max(lmax[lc],dis[pos[mid+1]]-dis[pos[l]]+lmax[rc]);
rmax[p]=max(rmax[lc]+dis[pos[r]]-dis[pos[mid]],rmax[rc]);
ans[p]=max(max(ans[lc],ans[rc]),rmax[lc]+cost[pos[mid+1]]+lmax[rc]);
}
void build(int& p,int l,int r)
{
p=++tot;
if (l==r)
{
if (col[pos[l]]) ans[p]=lmax[p]=rmax[p]=max(0,d(pos[l]));
else lmax[p]=rmax[p]=d(pos[l]),ans[p]=-INF;
return;
}
int mid=(l+r)>>1;
build(lc,l,mid);build(rc,mid+1,r);
update(p,l,r);
}
void modify(int p,int l,int r,int k)
{
if (l==r)
{
if (col[pos[l]]) ans[p]=lmax[p]=rmax[p]=max(0,d(pos[l]));
else lmax[p]=rmax[p]=d(pos[l]),ans[p]=-INF;
return;
}
int mid=(l+r)>>1;
if (k<=mid) modify(lc,l,mid,k);
else modify(rc,mid+1,r,k);
update(p,l,r);
}
void DFS(int u,int t)
{
if (!u) return;
dis[u]=dis[fa[u]]+cost[u];
bt[tp[u]=t]=pos[dfn[u]=++tim]=u;
DFS(son[u][0],t);DFS(son[u][1],son[u][1]);
if (u==t) build(rt[u],dfn[u],dfn[bt[u]]),re.insert(ans[rt[u]]);
}
int main()
{
freopen("test.in","r",stdin);
n=read();
int cnt=n;
for (int i=1;i<=n;i++) col[i]=1;
for (int i=1;i<n;i++)
{
int u,v,w;
u=read(),v=read(),w=read();
addnode(u,v,w),addnode(v,u,w);
}
dep[1]=1;
dfs(1);Dfs(1);DFS(1,1);
for (int i=read();i;--i)
{
if (gal()=='A') cnt? printf("%d\n",max(0,*re.rbegin())):puts("They have disappeared.");
else
{
int x=read();
for ((col[x]^=1)? ++cnt:--cnt;x;re.erase(re.find(ans[rt[tp[x]]])),modify(rt[tp[x]],dfn[tp[x]],dfn[bt[tp[x]]],dfn[x]),re.insert(ans[rt[tp[x]]]),x=fa[tp[x]]);
}
}
return 0;
}
做这种题一定要先理清思路,弄懂定义再开始码,不要边想边写