树上差分学习笔记

学习链接:https://www.sohu.com/a/271430685_100201031

树上差分的方式有两种:

一.点差分:改变树上路径(u,v)上的所有的点的点权值,假设增加了val值

我们对每一个节点维护一个tag数组作为差分数组

考虑改变的影响?

tag[u]+=val,tag[v]+=val,tag[lca(u,v)]-=val,tag[fa(lca(u,v))]-=val;

同一条路径上,我们改变端点的权值,这样分别从u,v回溯的时候,我们就可以求出这条路径上所有点的权值了!

然后考虑将fa[lca(u,v)]的权值减掉val,因为我们对链(u,v)的操作不应该对链外节点造成影响!

一般可以用倍增求LCA的方式处理

例题:

1.洛谷:P3128 [USACO15DEC]最大流Max Flow

CODE:

/*

*/
#include<cstdio>
using namespace std;
const int N=500020,logN=19;
struct node{
int from;
int to;
int next;
}e[N<<1];
int head[N],num,n,m,log[N],f[N][logN],dep[N],tag[N],ans;
inline int max(int x,int y)
{
return x>y?x:y;
}
void addedge(int from,int to)
{
e[++num].next=head[from];
e[num].to=to;
e[num].from=from;
head[from]=num;
}
void swap(int &a,int &b)
{
int c=a;
a=b;
b=c;
}
void init()
{
log[0]=-1;
for(int i=1;i<=n;i++)
log[i]=log[i>>1]+1;
return;
}
void dfs(int u,int fa)
{
tag[u]=0;
dep[u]=dep[fa]+1;
f[u][0]=fa;
for(int i=1;i<=log[dep[u]];i++)
f[u][i]=f[f[u][i-1]][i-1];

for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(v!=fa)
dfs(v,u);
}
}
int ask(int x,int y)
{
if(dep[x]<dep[y])
swap(x,y);
while(dep[x]>dep[y])
x=f[x][log[dep[x]-dep[y]]];
if(x==y)
return x;
for(int i=log[dep[x]];i>=0;i--)
{
if(f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}
void work(int u,int fa)
{
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(v!=fa)
{
work(v,u);
tag[u]+=tag[v];
}
}
ans=max(ans,tag[u]);
}
int main()
{
int x,y;
scanf("%d%d",&n,&m);
for(int i=1;i<=n-1;i++)
{
scanf("%d%d",&x,&y);
addedge(x,y);
addedge(y,x);
}
//LCA²¿·Ö
init();
dfs(1,0);
//Ê÷Éϲî·ÖµÄÏêϸ¹ý³Ì
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
tag[x]++;
tag[y]++;
int LCA=ask(x,y);
tag[LCA]--;
tag[f[LCA][0]]--;
}
work(1,0);//×îºó´Ó¸ù¿ªÊ¼Í³¼Æans,×¢ÒâÇ°ºóµÄ¸ù²»¿ÉÒÔ¸ü»»£¡
printf("%d",ans);
return 0;
}

二.边差分:

考虑对于一条链,将他的边权下放到深度较大的点上,这样可以保证每条边唯一对应一个点!

然后,求一条边的经过次数,对于路径(u,v),记tag[u]++,tag[v]++,tag[lca(u,v)]-=2;

因为lca(u,v)的路径是lca与其父亲的,没有被经过却被计算了两次,所以删去即可!

例题:运输计划

猜你喜欢

转载自www.cnblogs.com/little-cute-hjr/p/11787581.html
今日推荐