1. 程式人生 > 實用技巧 >樹 上 差分

樹 上 差分

改天補坑矩陣的差分

樹上差分:

模型:樹上多次區間修改.

差分適用於修改多而詢問少的情況

型別:1.邊差分 2.點差分

邊差分:把邊的路徑全部+x,把兩個點的\(u\)\(v的lca\)算出來,然後在差分陣列\(dlt[u]+=x,dlt[v]+=x\),在\(dlt[lca(u,v)]-=2x\)

點差分:多次把點的點權加x,最後問點權最大為多少,這就是P3128 USACO15DEC

做法在\(dlt[lca(u,v)]-=x\)並把\(dit[fa(lca(u,v))]-=x\). 因為\(lca(u,v)\)也在u...v這條路徑上,它同樣需要被加x.回溯的時候會從u和v兩個方向都給lca(u,v)加一個x,而它只能加一個,因此\(dlt[lca(u,v)]-=x\)

。而\(lca(u,v)\)的爸爸則根本無法被加,在\(lca(u,v)\)已經只加一個x了,因此\(dlt[fa[lca(u,v)]]-=x\)就能讓\(lca(u,v)\)的爸爸不加x

#include<cstdio>
#include<iostream> 
#define re register
#define ll long long 
#define maxn 50005
using namespace std;
struct edge{
	int to,next;
}e[maxn<<2];
inline int read(){
	int s=0;
	char c=getchar();
	while (c<'0' || c>'9') c=getchar();
	while (c>='0' && c<='9') s=s*10+c-'0',c=getchar();
	return s;
}
int head[maxn],power[maxn],n,m,d[maxn],fa[maxn][22],ans,tot;
inline void add(int u,int v){e[++tot].to = v;e[tot].next = head[u];head[u] = tot;}
inline void dfs(int u,int fath){
	d[u]=d[fath]+1,fa[u][0]=fath;
	for (re int i=0;fa[u][i];++i) fa[u][i+1]=fa[fa[u][i]][i];
	for (re int i=head[u];i;i=e[i].next){
		int v=e[i].to;
		if (v!=fath) dfs(v,u);
	}
	return ;
}
inline int lca(int u,int v){
	if(d[u] > d[v]) std::swap(u,v);
	for(re int i = 20;i >= 0;i--) if(d[u] <= d[v] - (1<<i)) v = fa[v][i];
	if(u == v) return u;
	for(re int i = 20;i >= 0;i--) if(fa[u][i] != fa[v][i]) u = fa[u][i],v = fa[v][i];
	return fa[u][0];
}
inline void get(int u,int f){
	for(re int i = head[u];i;i = e[i].next){
		int v = e[i].to;
		if(v == f) continue;
		get(v,u);
		power[u] += power[v];
	}
	ans = std::max(ans,power[u]);
}
int main(){
	n = read();m = read();
	int x,y;
	for(re int i = 1;i <= n - 1;i++){
		x = read(), y = read();
		add(x,y); add(y,x);
	}
	dfs(1,0);
	for(re int i = 1;i <= m;i++){
		x = read(), y = read();
		int LCA = lca(x,y);
		++power[x];++power[y];--power[LCA];--power[fa[LCA][0]];
	}
	get(1,0);
	printf("%d\n",ans);
	return 0;
}

改日補個邊差分 咕咕咕