[LCA 树上差分边差分] 闇の連鎖
Posted 鱼竿钓鱼干
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[LCA 树上差分边差分] 闇の連鎖相关的知识,希望对你有一定的参考价值。
[LCA 树上差分边差分] 闇の連鎖
题目
思路
主要参考蓝书
这题实际上是统计边被覆盖的次数
附加边(x,y)的效果就是把树上路径(x,y)的边全部+1
我们可以使用树上差分做标记来优化
void Edge_Mark(int x,int y,int v){//边差分标记
int lca=LCA(x,y);
mark[x]+=v;mark[y]+=v;mark[lca]-=2*v;
}
回溯统计子树和可以知道每条边的被覆盖次数
求解答案贡献的时候分类讨论
主要边被覆盖0次,附加边随便选
主要边被覆盖1次,附加边唯一确定
主要边被覆盖2次,无可行方案
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,M=25;
int n,m,ans;
vector<int>root[N];
int fa[N][M],mark[N],LG[N],dep[N];
void prework(){
LG[1]=0;
for(int i=2;i<=N;i++)LG[i]=LG[i/2]+1;
}
void dfs(int u,int v){
dep[u]=dep[v]+1;
fa[u][0]=v;
for(auto to:root[u])
if(to!=v)dfs(to,u);
}
int LCA(int x,int y){
if(dep[x]<dep[y])swap(x,y);
while(dep[x]>dep[y])
x=fa[x][LG[dep[x]-dep[y]]];
if(x==y)return y;
for(int i=20;i>=0;i--)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
void Edge_Mark(int x,int y,int v){//边差分标记
int lca=LCA(x,y);
mark[x]+=v;mark[y]+=v;mark[lca]-=2*v;
}
int get_ans(int u,int v){
int res=mark[u];
for(auto to:root[u]){
if(to!=v){
int s=get_ans(to,u);//获取子树边中被附加边覆盖的次数
if(s==0)ans+=m;//如果被覆盖数为0,那么任意附加边都可以切
else if(s==1)ans++;//如果被覆盖数为1,那么被切掉的附加边是唯一确定的
//其他情况没法实现
res+=s;
}
}
return res;
}
int main(){
cin>>n>>m;
for(int i=1;i<n;i++){
int x,y;cin>>x>>y;
root[x].push_back(y);
root[y].push_back(x);
}
dfs(1,0);
for(int j=1;j<=20;j++)
for(int i=1;i<=n;i++)
fa[i][j]=fa[fa[i][j-1]][j-1];
for(int i=1;i<=m;i++){
int x,y;cin>>x>>y;
Edge_Mark(x,y,1);
}
get_ans(1,0);
cout<<ans;
return 0;
}
以上是关于[LCA 树上差分边差分] 闇の連鎖的主要内容,如果未能解决你的问题,请参考以下文章
[填坑]树上差分 例题:[JLOI2014]松鼠的新家(LCA)