树上差分

一、点差分

    对于树上路径 p a t h ( u , v ) path(u,v) path(uv)
     d l t [ u ] + + , d l t [ v ] + + , d l t [ l c a ( u , v ) ] − − , d l t [ f ( l c a ( u , v ) ) ] − − dlt[u]++, dlt[v]++, dlt[lca(u, v)] --, dlt[f(lca(u, v))] -- dlt[u]++dlt[v]++dlt[lca(u,v)]dlt[f(lca(u,v))]

    询问 x x x 被多少个标记覆盖 d f s dfs dfs
    从根节点开始,将其本身的权值加上所有子节点的权值
    每个节点的权值既是其被路径覆盖的次数

洛谷 P3128 最大流

模板:

#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
const ll mod = 1e9 + 7;
const int maxn = 3e5 + 10;
const int maxm = 2e5 + 10;
int n, k, f[maxn][25];
int cnt, head[maxn];
int dep[maxn], dlt[maxn];

struct EDGE {
	int next, to, w;
} edge[maxn<<2];

void add(int u, int v, int w) {
	edge[++cnt].next = head[u];
	edge[cnt].to = v;
	edge[cnt].w = w;
	head[u] = cnt;
}

void dfs1(int cur, int fa, int de){
	dep[cur] = de;
	f[cur][0] = fa;
	for(int i=1; i<=20; i++) 
		f[cur][i] = f[f[cur][i-1]][i-1];
	for(int i=head[cur]; i; i=edge[i].next){
		if(edge[i].to == fa) continue;
		dfs1(edge[i].to, cur, de+1);
	}
}

int lca(int x, int y){
	if(dep[x]<dep[y]) swap(x, y);
	for(int i=20; i>=0; i--)
		if(dep[f[x][i]]>=dep[y])
			x = f[x][i];
	if(x == y) return x;
	for(int i=20; i>=0; i--)
		if(f[x][i] ^ f[y][i])
			x=f[x][i], y=f[y][i];
	return f[x][0];
}

void dfs2(int u){
	for(int i=head[u]; i; i=edge[i].next){
		if(edge[i].to==f[u][0]) continue;
		dfs2(edge[i].to);
		dlt[u] += dlt[edge[i].to];
	}
}

int main() {
	scanf("%d%d", &n, &k);
	for(int i=1; i<n; i++){
		int u, v;
		scanf("%d%d", &u, &v);
		add(u, v, 1);
		add(v, u, 1);
	} 
	dfs1(1, 0, 1);

	for(int i=0, u, v; i<k; i++){
		scanf("%d%d", &u, &v);
		int fa = lca(u, v);
		dlt[u]++, dlt[v]++;
		dlt[fa]--;
		dlt[f[fa][0]]--;
	}
	dfs2(1);
	
	int ans = 0;
	for(int i=1; i<=n; i++) ans = max(ans, dlt[i]);
	printf("%d\n", ans);
}

二、边差分

    对于树上路径 p a t h ( u , v ) path(u,v) path(uv)
     d l t [ u ] + + , d l t [ v ] + + , d l t [ l c a ( u , v ) ] − = 2 dlt[u]++, dlt[v]++, dlt[lca(u, v)] -=2 dlt[u]++dlt[v]++dlt[lca(u,v)]=2

    询问 x x x 与其父亲的连边被多少个标记覆盖 d f s dfs dfs
    从根节点开始,将其本身的权值加上所有子节点的权值
    每个节点的权值即表示与其父亲的连边,被路径覆盖的次数

例题一:洛谷 P2680 运输计划

模板:

#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
const ll mod = 1e9 + 7;
const int maxn = 3e5 + 10;
int n, m, f[maxn][25];
int cnt, head[maxn], dep[maxn];
int a[maxn], b[maxn], l[maxn];
int dis[maxn], dist[maxn];
int rk[maxn], rks, topre[maxn];
int maxjob, dlt[maxn];

struct EDGE {
	int next, to, w;
} edge[maxn<<2];

void add(int u, int v, int w) {
	edge[++cnt].next = head[u];
	edge[cnt].to = v;
	edge[cnt].w = w;
	head[u] = cnt;
}

void dfs(int cur, int fa, int de){
	rk[++rks] = cur;
	dep[cur] = de;
	f[cur][0] = fa;
	for(int i=1; i<=20; i++)
		f[cur][i] = f[f[cur][i-1]][i-1];
	for(int i=head[cur]; i; i=edge[i].next){
		if(edge[i].to == fa) continue;
		topre[edge[i].to] = edge[i].w;
		dis[edge[i].to] = dis[cur] + edge[i].w;
		dfs(edge[i].to, cur, de+1);
	}
}

int lca(int x, int y){
	if(dep[x]<dep[y]) swap(x, y);
	for(int i=20; i>=0; i--)
		if(dep[f[x][i]]>=dep[y])
			x = f[x][i];
	if(x == y) return x;
	for(int i=20; i>=0; i--)
		if(f[x][i] ^ f[y][i])
			x=f[x][i], y=f[y][i];
	return f[x][0];
}

bool check(int k){
	memset(dlt, 0, sizeof(dlt));
	int tot = 0;
	for(int i=1; i<=m; i++)
		if(dist[i] > k){
			dlt[a[i]]++, dlt[b[i]]++;
			dlt[l[i]] -= 2, tot++;
		}
	for(int i=n; i; i--){
		dlt[f[rk[i]][0]] += dlt[rk[i]];
		if(topre[rk[i]]>=maxjob-k && dlt[rk[i]]==tot)
			return true;
	}
	return false;
}

int main() {
	scanf("%d%d", &n, &m);
	for(int i=1; i<n; i++){
		int u, v, w;
		scanf("%d%d%d", &u, &v, &w);
		add(u, v, w);
		add(v, u, w);
	}
	dfs(1, 0, 1);
	for(int i=1; i<=m; i++){
		scanf("%d%d", a+i, b+i);
		l[i] = lca(a[i], b[i]);
		dist[i] = dis[a[i]] + dis[b[i]] - 2*dis[l[i]];
		maxjob = max(maxjob, dist[i]);
	}
	int l = 0, r = maxjob, mid;
	while(l<=r){
		mid = l + r >> 1;
		if(check(mid)) r = mid - 1;
		else l = mid + 1;
	}
	printf("%d\n", l);
}

例题二:POJ 3417

模板:

#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
const ll mod = 1e9 + 7;
const int maxn = 3e5 + 10;
int n, m, f[maxn][25];
int cnt, head[maxn], dep[maxn]; 
int rk[maxn], rks, dlt[maxn];

struct EDGE {
	int next, to, w;
} edge[maxn<<2];

void add(int u, int v, int w) {
	edge[++cnt].next = head[u];
	edge[cnt].to = v;
	edge[cnt].w = w;
	head[u] = cnt;
}

void dfs(int cur, int fa, int de){
	rk[++rks] = cur;
	dep[cur] = de;
	f[cur][0] = fa;
	for(int i=1; i<=20; i++)
		f[cur][i] = f[f[cur][i-1]][i-1];
	for(int i=head[cur]; i; i=edge[i].next){
		if(edge[i].to == fa) continue;
		dfs(edge[i].to, cur, de+1);
	}
}

int lca(int x, int y){
	if(dep[x]<dep[y]) swap(x, y);
	for(int i=20; i>=0; i--)
		if(dep[f[x][i]]>=dep[y])
			x = f[x][i];
	if(x == y) return x;
	for(int i=20; i>=0; i--)
		if(f[x][i] ^ f[y][i])
			x=f[x][i], y=f[y][i];
	return f[x][0];
}

int main() {
	scanf("%d%d", &n, &m);
	for(int i=1; i<n; i++){
		int u, v;
		scanf("%d%d", &u, &v);
		add(u, v, 1);
		add(v, u, 1);
	}
	dfs(1, 0, 1);
	for(int i=0; i<m; i++){
		int u, v;
		scanf("%d%d", &u, &v);
		dlt[u]++, dlt[v]++;
		dlt[lca(u, v)] -= 2;
	}
	for(int i=n; i; i--)
		dlt[f[rk[i]][0]] += dlt[rk[i]];
	int ans = 0;
	for(int i=2; i<=n; i++){
		if(dlt[rk[i]] == 0) ans += m;
		else if(dlt[rk[i]] == 1) ans++;
	}
	printf("%d\n", ans);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值