树上差分
多次对树上的一些路径做加法操作,然后询问某个点或某条边经过操作后的值,就要考虑树上差分了。
点差分
模拟这个过程
对x到y路径上的点权值均+1,可以等价成对x和y的权值加1,对lca的权值-1,对fa[lca]的权值-1
- 遍历到x,权值为1
- 回溯到4,通过递归求得子树和,得到权值为1,
- 遍历到7,再回溯回4,权值不变
- 回溯到3,权值-1+1为0
- 遍历到5,再遍历到y,y权值为1
- 回溯到5,权值+1为1
- 回溯到3,权值为0+1=1
- 再回溯到2,权值为-1+1=0
故做到了通过一趟dfs就可以求出权值,且不影响其他无关的点
边差分
这个模拟的过程和点差分差不多,不过这里边的权值是映射到边下面那个点上,如这个操作是
- x的权值+1,即4到6这条边的权值+1
- y的权值+1,即5到8这条边的权值+1
- lca的权值-2
砍树【蓝桥杯】例题
思路:题目说要砍掉边,其实就是这条边是这些路径的公共边,且走了m次,用边差分,cnt[i]表示编号为i的边走过多少次,即权值,e[i]表示标号为i的点对应的边
#include<iostream>
#include<vector>
using namespace std;
typedef long long LL;
vector<pair<int,int>> v[100005];
LL cnt[100005],f[100005][21],dep[100005],e[100005];
void dfs(int u,int fa)
{dep[u]=dep[fa]+1;f[u][0]=fa;for(int i=1;(1<<i)<=dep[u];i++) f[u][i]=f[f[u][i-1]][i-1];for(auto &p:v[u]){if(p.first==fa) continue;dfs(p.first,u);//给点赋值对应的边e[p.first]=p.second;}
}
//求LCA
int lca(int u,int v)
{if(dep[u]<dep[v]) swap(u,v);for(int i=20;i>=0;i--){if(dep[f[u][i]]>=dep[v]) u=f[u][i];if(u==v) return u;}for(int i=20;i>=0;i--){if(f[u][i]!=f[v][i]) {u=f[u][i];v=f[v][i];}}return f[u][0];
}
void dfs2(int u)
{for(auto &p:v[u]){if(p.first==f[u][0]) continue;dfs2(p.first);cnt[e[u]]+=cnt[e[p.first]];}
}
int main()
{int n,m;cin>>n>>m;for(int i=1;i<n;i++){int a,b;cin>>a>>b;//记录边及编号v[a].push_back({b,i});v[b].push_back({a,i});}dfs(1,0);for(int i=0;i<m;i++){int a,b;cin>>a>>b;//树上差分边差分操作cnt[e[a]]++;cnt[e[b]]++;cnt[e[lca(a,b)]]-=2;}//遍历回溯dfs2(1);int ans=0;for(int i=1;i<n;i++){if(cnt[i]==m) ans=i;}cout<<ans<<endl;return 0;
}