算法随笔——树上问题若干

树上问题若干

树上差分

即在树上进行差分。

例题 1 (点差分)

P3128 [USACO15DEC] Max Flow P

题意:对树上一条路径 + 1,求最大点权值。

做法

设操作 s -> t 路径,则设差分数组 d
操作为

d[s]++,d[t]++,d[lca(s,t)]--,d[fa[lca(s,t)]]--;

操作完后,每个点真实权值为其子树 d 的和。

例题二(边差分)

P2680 [NOIP2015 提高组] 运输计划

题意:给出一棵树和 m 条路径,可以将一条边权改为 0,问这些路径最大值最小是多少?

首先先二分答案,然后将求所有长度大于 mid 的路径的并,然后选其中边权最大的边。

找并可以用树上差分,边权可以转化为这条边 dep 较小的点的点权。

若路径为 st

操作为

d[s]++,d[t]++,d[lca(s,t)]-=2;

树链剖分

oiwiki

image

例题P3178

P3178

模版。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
#define re register
#define int ll
#define PII pair<int,int>
int read()
{
	int f=1,k=0;char c = getchar();
	while(c <'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')k=(k<<1)+(k<<3)+(c^48),c=getchar();
	return k*f;
}

const int N = 4e5+5;

int n,m;
vector<int> v[N];

struct node
{
	int sum,tag;
}tr[N];

int a[N];
int fa[N],top[N],son[N],siz[N],dep[N],id[N],dfn[N];
int tot;

void dfs1(int x,int fat)
{
	fa[x] = fat;
	siz[x] = 1;
	dep[x] = dep[fat] + 1;
	int maxn = 0;
	for (auto y :v[x])
	{
		if (y == fat) continue;
		dfs1(y,x);
		siz[x] += siz[y];
		if (siz[y] > maxn) maxn = siz[y],son[x] = y; //子节点size最大的为重儿子
	}
}

void dfs2(int x,int tp)
{
	top[x] = tp;
	dfn[x] = ++tot;
	id[tot] = x;
	if (son[x]) dfs2(son[x],tp); //提前遍历重儿子
	for (auto y : v[x])
		if (y !=fa[x] && y != son[x]) dfs2(y,y);
}

void pushdown(int k,int l,int r)
{
	if (tr[k].tag)
	{
		int mid = l + r >> 1;
		tr[k<<1].tag += tr[k].tag;
		tr[k<<1|1].tag += tr[k].tag;
		tr[k<<1].sum += tr[k].tag * (mid-l+1);
		tr[k<<1|1].sum += tr[k].tag * (r-mid);
		tr[k].tag = 0;
	}
}

void pushup(int k)
{
	tr[k].sum = tr[k<<1].sum + tr[k<<1|1].sum;
}

void modify(int k,int l,int r,int x,int y,int v)
{
	if (x >r || y <l) return;
	if (x <= l &&r <= y) 
	{
		tr[k].sum += (r-l+1) * v;
		tr[k].tag += v;
		return;
	}
	int mid = l +r >> 1;
	pushdown(k,l,r);
	modify(k<<1,l,mid,x,y,v);
	modify(k<<1|1,mid+1,r,x,y,v);
	pushup(k);
}

int query(int k,int l,int r,int x,int y)
{
	if (x > r || y <l ) return 0;
	if (x <= l && r <= y) return tr[k].sum;
	pushdown(k,l,r);
	int mid =l  +r >> 1;
	return query(k<<1,l,mid,x,y) +query(k<<1|1,mid+1,r,x,y);
}

int qsum(int x,int y)
{
	int ans = 0;
	while (top[x] != top[y]) //top[x] == top[y] 表示在同一条链上
	{
		if (dep[top[x]] < dep[top[y]]) swap(x,y);
		ans += query(1,1,n,dfn[top[x]],dfn[x]);
		x = fa[top[x]];		
	}
	if (dep[x] < dep[y]) swap(x,y);
	ans += query(1,1,n,dfn[y],dfn[x]);
	return ans;
}

signed main()
{
	cin >> n >>m;
	for (int i = 1;i <= n;i++) a[i] = read();
	for (int i = 1;i <= n-1;i++)
	{
		int x = read(),y = read();
		v[x].push_back(y);
		v[y].push_back(x);
	}
	dfs1(1,0);
	dfs2(1,1);
	for (int i = 1;i <= n;i++) modify(1,1,n,dfn[i],dfn[i],a[i]);
	for (int i = 1;i <= m;i++)
	{
		int op = read(),x = read();
		if (	op == 1)
		{
			int v = read();
			modify(1,1,n,dfn[x],dfn[x],v);
		}
		else if (op == 2)
		{
			int v = read();
			modify(1,1,n,dfn[x],dfn[x] + siz[x] - 1,v);
		}
		else{
			cout <<qsum(x,1) << endl;
		}
		
	}
	
	return 0;
}

P2486

P2486

题意:树上路径推平,树上路径查询颜色段数量。

显然可以熟练剖分转化为区间问题,然后对于每个区间维护左边颜色和右边颜色和答案。

唯一需要注意的细节是树链剖分中链与链之间也需要计算贡献。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
#define re register
#define PII pair<int,int>
int read()
{
	int f=1,k=0;char c = getchar();
	while(c <'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')k=(k<<1)+(k<<3)+(c^48),c=getchar();
	return k*f;
}

const int N = 4e5+5;


int top[N],dfn[N],tot,fa[N],siz[N],dep[N],son[N];
int n,q,a[N];
int id[N];
vector<int> v[N];

struct node
{
	int ans,lcol,rcol,tag;
}tr[N];

void dfs1(int x,int fat)
{
	fa[x] = fat;
	dep[x] = dep[fat] + 1;
	siz[x] = 1;
	int maxn = 0;
	for (auto y : v[x])
	{
		if (y == fat) continue;
		dfs1(y,x);
		siz[x] += siz[y];
		if (siz[y] > maxn) maxn = siz[y],son[x] = y;
	}
}

void dfs2(int x,int tp)
{
	top[x] = tp;
	dfn[x] = ++tot;
	id[tot] = x;
	if(son[x]) dfs2(son[x],tp);
	for (auto y : v[x])
	{
		if (y == fa[x] || y == son[x]) continue;
		dfs2(y,y);
	}
}




void update(node &a,node q1,node q2)
{
	a.lcol = q1.lcol;
	a.rcol = q2.rcol;
	a.ans = q1.ans + q2.ans - (q1.rcol == q2.lcol);
}

void pushdown(int k)
{
	if (tr[k].tag)
	{
		tr[k<<1].tag = tr[k<<1|1 ].tag = tr[k].tag;
		tr[k<<1].lcol = tr[k<<1].rcol = tr[k<<1|1].lcol = tr[k<<1|1].rcol = tr[k].tag;
		tr[k<<1].ans = tr[k<<1|1].ans = 1;
		tr[k].tag = 0;
	}
}

void modify(int k,int l,int r,int x,int y,int c)
{
	if (x > r || y < l) return;
	if (x <= l && r <= y) 
	{
		tr[k].ans = 1,tr[k].lcol = c,tr[k].rcol = c ,tr[k].tag = c;
		return;
	}
	int mid = l +r >> 1;
	pushdown(k);
	modify(k<<1,l,mid,x,y,c);
	modify(k<<1|1,mid+1,r,x,y,c);
	update(tr[k],tr[k<<1],tr[k<<1|1]);
}
node kb;
node query(int k,int l,int r,int x,int y)
{
		
	if(x <= l &&r  <= y) return tr[k];
	int mid = l + r >> 1;
	pushdown(k);
	if (x > mid) return query(k<<1|1,mid+1,r,x,y);
	if (y < mid+1) return query(k<<1,l,mid,x,y);
	node q1 = query(k<<1,l,mid,x,y),q2 = query(k<<1|1,mid+1,r,x,y),res;
	update(res,q1,q2);
	return res;
}
int qsum(int x,int y)
{
	int zb = 0,yb = 0;
	int ans = 0;
	while (top[x] != top[y])
	{
		node res;
		if (dep[top[x]] > dep[top[y]])
		{
			res = query(1,1,n,dfn[top[x]],dfn[x]);
			if (res.rcol == zb || zb==0) res.ans-=(zb!=0);
			zb = res.lcol;
			x = fa[top[x]];
		}
		else{
			res = query(1,1,n,dfn[top[y]],dfn[y]);
			if (res.rcol == yb || yb == 0) res.ans-=(yb!=0);
			yb = res.lcol;
			y = fa[top[y]];
		}
		ans += res.ans;
		
	}
	node res;
//	int ans = 0;
//	cout << zb <<' ' << yb <<endl;
	if (dep[x] > dep[y])
	{
		res = query(1,1,n,dfn[y],dfn[x]);//cout << y <<' ' << x <<endl;

		if (res.rcol == zb) res.ans--;
		if (res.lcol == yb) res.ans--;
	//	ans += res.ans;
	}
	else{
		res = query(1,1,n,dfn[x],dfn[y]);

		if (res.rcol == yb) res.ans--;
		if (res.lcol == zb) res.ans--;
	}

	ans += res.ans;
	return ans;
}

void change(int x,int y,int c)
{
	while (top[x] != top[y])
	{
		if (dep[top[x]] < dep[top[y]] ) swap(x,y);

		modify(1,1,n,dfn[top[x]],dfn[x],c);
		x = fa[top[x]];
	}
	if (dfn[x] > dfn[y]) swap(x,y);
	modify(1,1,n,dfn[x],dfn[y],c);

	
}

void build(int k,int l,int r)
{
	if (l == r)
	{
		tr[k].ans = 1;tr[k].rcol = tr[k].lcol = a[id[l]];tr[k].tag = 0;
	///	cout <<l <<' ' << a[id[l]] <<endl;
		return;
	}
	int mid= l +r >> 1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	update(tr[k],tr[k<<1],tr[k<<1|1]);

}
int main()
{
	cin >> n >> q;
	for (int i =1;i <= n;i++) a[i] = read();
	
	for (int i = 1;i < n;i++)
	{
		int x= read(),y = read();
		v[x].push_back(y);
		v[y].push_back(x);
	}
	dfs1(1,0);
	dfs2(1,1);
	build(1,1,n);

	while (q--)
	{
		char op;cin >> op;
		int x = read(),y = read();
		if (op == 'Q')
		{
		//	cout <<top[x] << " "<<top[y] << endl;
			 
			cout << qsum(x,y) <<endl;
		
		}
		else{
			int c=  read();
			change(x,y,c);
		}
	}
	
	
	return 0;
}

比较考验码力。

posted @   codwarm  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
· 2分钟学会 DeepSeek API,竟然比官方更好用!
· .NET 使用 DeepSeek R1 开发智能 AI 客户端
· DeepSeek本地性能调优
· 一文掌握DeepSeek本地部署+Page Assist浏览器插件+C#接口调用+局域网访问!全攻略
点击右上角即可分享
微信分享提示