传送门
题意:
给你一颗有nnn个结点的树以及mmm个路径。对于每一个路径pathipath_ipathi,代表着你将会从uiu_iui走到viv_ivi。现在问你,你走完着mmm个路径后,在这nnn个结点中经过的最多的次数。
题目分析:
首先,如果我们用dfsdfsdfs在树上暴力去跑的话,显然时间肯定是接受不了的。因此我们需要考虑一种较为优美的算法。
我们发现,经过每一个路径pathipath_ipathi,本质上是使路径pathipath_ipathi上的所有的点权+1+1+1,如果这个问题是在序列上的话,我们直接就可以用线段树或差分树状数组去做。而正因为这个问题是在树上进行的,因此为了简化问题,我们则需要将树形的结构转化成链式结构。
因此根据转化的不同,该问题可以通过树链剖分或者树上差分去解决。
而显然树上差分相对来说比较简单。
我们只需要令cnt[ui]+1cnt[u_i]+1cnt[ui]+1,cnt[vi]+1cnt[v_i]+1cnt[vi]+1,并且令cnt[ lca[ui,vi] ]−1cnt[~lca[u_i,v_i]~]-1cnt[ lca[ui,vi] ]−1,cnt[ fa[ lca[ui,vi] ] ]−1cnt[~fa[~lca[u_i,v_i]~] ~]-1cnt[ fa[ lca[ui,vi] ] ]−1即可。如果我们采用上述差分方法,在最后我们进行dfsdfsdfs回溯的时候,当前结点uuu就能够获得他的儿子们的权值。
整体的时间复杂度:O(nlogn)\mathcal{O}(nlogn)O(nlogn)
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=500005;
const int LOG=20;
struct Node{
int to,next;
}q[maxn<<1];
int head[maxn],cnt=0;
void add_edge(int from,int to){
q[cnt].to=to;
q[cnt].next=head[from];
head[from]=cnt++;
}
struct LCA{//倍增lca
int anc[maxn][LOG],depth[maxn];
void dfs(int x,int fa,int dis){
anc[x][0]=fa;depth[x]=dis;
for(int i=head[x];i!=-1;i=q[i].next){
int to=q[i].to;
if(to==fa) continue;
dfs(to,x,dis+1);
}
}
void init(int root,int n){
dfs(root,-1,1);
for(int j=1;j<LOG;j++){
for(int i=1;i<=n;i++){
anc[i][j]=anc[ anc[i][j-1] ][j-1];
}
}
}
void swim(int &x,int h){
for(int i=0;h>0;i++){
if(h&1)
x=anc[x][i];
h>>=1;
}
}
int query(int x,int y){
if(depth[x]<depth[y]) swap(x,y);
swim(x,depth[x]-depth[y]);
if(x==y) return x;
for(int i=LOG-1;i>=0;i--){
if(anc[x][i]!=anc[y][i]){
x=anc[x][i];
y=anc[y][i];
}
}
return anc[x][0];
}
}lca;
int Bit[maxn];
void Search(int x,int fa){//最优一次dfs获取权值
for(int i=head[x];i!=-1;i=q[i].next){
int to=q[i].to;
if(to==fa) continue;
Search(to,x);
Bit[x]+=Bit[to];
}
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
memset(head,-1,sizeof(head));
cnt=0;
for(int i=1;i<n;i++){
int from,to;
scanf("%d%d",&from,&to);
add_edge(from,to);
add_edge(to,from);
}
lca.init(1,n);
while(m--){
int l,r;
scanf("%d%d",&l,&r);
Bit[l]++,Bit[r]++;//差分
int x=lca.query(l,r);
Bit[x]--;
Bit[lca.anc[x][0]]--;
}
Search(1,-1);
int maxx=0;
for(int i=1;i<=n;i++){
maxx=max(maxx,Bit[i]);
}
printf("%d\n",maxx);
return 0;
}
本文介绍了一种通过树上差分解决特定树形结构路径计数问题的方法。利用LCA算法和差分技巧,实现了O(nlogn)的时间复杂度。
2387

被折叠的 条评论
为什么被折叠?



