先推荐一个菊苣的博客讲解 。
对于这种查询树链,修改树边的问题,我们可以使用树链剖分。
首先处理出重边和轻边:
① u 和 son[u] 为重边,当且仅当 son[u] 是 u 的所有点中,其子树点数最多的。
②不是重边的边为轻边。
由重边形成的链为重链。
性质:
①从根节点到任意点的轻边数量为 O(logn) 。
证明:轻边的两端点的子树点数之差至少为2倍。
②从根节点到任意点的重边数量为 O(logn) 。
证明:轻边和重链是交错的,因此其数量之差至多为 1 。
处理出轻边和重链可以通过两次 dfs
实现。
第一次处理出重边和轻边,第二次处理出重链。
将每条重链维护在线段树上,这样就可以方便查询了。
对于每一次查询(如查询两点之间路径上的最大值):
①如果两点在同一条重链上,可以直接通过线段树查询,每次查询的时间复杂度是 O(logn) (好像网上的题解一般都是讲所有重链维护在一棵线段树上,我觉得可以对每条重链维护一棵线段树。。。不过可能优化不了多少)。
②如果两点不在同一条重链上,我们可以通过类似倍增的方法将两点向上提,从而使其在同一条重链上,这样就转到了①,复杂度是 O(log2n) 。
对于每一次修改:
①如果修改的是轻边,直接修改就行,复杂度是 O(1) 。
①如果修改的是重边,则直接用线段树就行,复杂度是 O(logn) 。
因此树链剖分的复杂度是 O(nlogn+qnlog2n) 。
#include
using namespace std;
const int N=1e4+7;
int sz[N],fa[N],top[N],son[N],d[N],id[N],n,cnt,lt[N];
vector<int> adj[N];
struct Edge
{
int u,v,c;
}es[N];
int tree[N<<2];
void dfs1(int u, int father, int deep)
{
d[u]=deep;
sz[u]=1;
fa[u]=father;
for(int i=0;iint v=adj[u][i];
if(v!=father)
{
dfs1(v,u,deep+1);
sz[u]+=sz[v];
if(son[u]==-1||sz[v]>sz[son[u]]) son[u]=v;
}
}
}
void dfs2(int u)
{
id[u]=++cnt;
if(son[u]!=-1)
{
top[son[u]]=top[u];
dfs2(son[u]);
}
for(int i=0;iint v=adj[u][i];
if(v!=fa[u]&&v!=son[u])
{
top[v]=v;
dfs2(v);
}
}
}
void update(int rt, int l, int r,int q, int val )
{
if(l==q&&r==q)
{
tree[rt]=val;
return ;
}
int mid=(l+r) >> 1;
if(q <= mid) update(rt<<1, l, mid, q, val);
else update(rt<<1|1, mid+1, r, q , val);
tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);
}
int query(int rt,int l,int r,int ql,int qr)
{
if(l>=ql&&r<=qr)
{
return tree[rt];
}
int mid=(l+r)>>1;
if(ql>mid) return query(rt<<1|1,mid+1,r,ql,qr);
if(qr<=mid) return query(rt<<1,l,mid,ql,qr);
return max(query(rt<<1,l,mid,ql,qr),query(rt<<1|1,mid+1,r,ql,qr));
}
void init()
{
cnt=0;
memset(son,-1,sizeof(son));
for(int i=1;i<=n;i++) adj[i].clear();
top[1]=1;
}
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
scanf("%d",&n);
init();
int u,v,c;
for(int i=1;iscanf("%d%d%d",&u,&v,&c);
es[i].u=u;
es[i].v=v;
es[i].c=c;
adj[u].push_back(v);
adj[v].push_back(u);
}
dfs1(1,0,1);
dfs2(1);
for(int i=1;iint u=es[i].u,v=es[i].v;
if(top[u]==top[v])
{
if(d[u]>d[v]) swap(u,v);
update(1,1,n,id[u],es[i].c);
}
else
{
if(d[u]char s[10];
while(~scanf("%s",s))
{
if(s[0]=='D') break;
if(s[0]=='Q')
{
int u,v;
scanf("%d%d",&u,&v);
int ans=0;
while(top[u]!=top[v])
{
if(d[top[u]]if(u!=top[u])
ans=max(ans,query(1,1,n,id[top[u]],id[u]-1));
u=top[u];
ans=max(ans,lt[u]);
u=fa[u];
}
if(d[u]>d[v]) swap(u,v);
if(u!=v) ans=max(ans,query(1,1,n,id[u],id[v]-1));
printf("%d\n",ans);
}
else
{
int x,c;
scanf("%d%d",&x,&c);
int u=es[x].u,v=es[x].v;
if(d[u]>d[v]) swap(u,v);
if(top[u]==top[v])
update(1,1,n,id[u],c);
else lt[v]=c;
}
}
}
return 0;
}