DDP

线段树+树剖/lct维护广义矩阵乘法

从例题开始讲

P4719

如果不带修改,那么就好做了

fi,1/0 表示 i 节点选或不选的最大权

容易得到转移

fi,0=sonmax(fson,0,fson,1)

fi,1=wi+sonfson,0

但是现在带修。

你会发现,一次修改只会影响一条链上的值,但不幸的是暴力修改仍是不可接受的

有什么方法可以快速处理树链?树剖,(LCT)

现在的问题是如何快速维护一条树链的 DP

我们先引入 广义矩阵乘法

image

那么这根我们的 DP 有什么关系呢?

我们尝试将 DP 写成矩阵的形式

为了保证复杂度,我们定义 gi,0/1 表示只考虑 i 的轻儿子, i 选或不选的最大值

那么有 (son 为重儿子)

fi,0=gi,0+max(fson,1,fson,0)

fi,1=gi,1+fson,0

把他写成广义矩阵

[gi,0gi,0gi,1]×[fson,0fson,1]=[fi,0fi,1]

使用树剖+线段树维护区间转移矩阵,就能快速转移一条重链,而修改只需修改他的 gi,1和向上的一条链

image

code
#include<cstring>
#include<algorithm>
#include<queue>
#include<iostream>
#include<cmath>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int read(){
	int x = 0; bool f = 0;char c = getchar();
	while(!isdigit(c)){f = c == '-';c = getchar();}
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return f ? -x : x;
}
const int maxn = 100005;

struct matrix{
	int a[2][2];
	matrix(){memset(a, -0x3f, sizeof(a));}
	matrix operator *(const matrix b){
		matrix ans;
		for(int i = 0; i < 2; ++i)
			for(int k = 0; k < 2; ++k)
				for(int j = 0; j < 2; ++j)
					ans.a[i][j] = max(ans.a[i][j], a[i][k] + b.a[k][j]);
		return ans;
	}
};
int n, m, a[maxn];
int head[maxn], tot;
struct edge{int to, net;}e[maxn << 1 | 1];
void add(int u, int v){
	e[++tot].net = head[u];
	head[u] = tot;
	e[tot].to = v;
}
int son[maxn], fa[maxn], top[maxn], ed[maxn], size[maxn], dep[maxn];
matrix val[maxn];
int f[maxn][2];

void dfs1(int x){
	size[x] = 1;
	for(int i = head[x]; i; i = e[i].net){
		int v = e[i].to;
		if(v == fa[x])continue;
		dep[v] = dep[x] + 1, fa[v] = x;
		dfs1(v);
		size[x] += size[v];
		son[x] = size[son[x]] > size[v] ? son[x] : v;
		f[x][0] += max(f[v][0], f[v][1]);
		f[x][1] += f[v][0];
	}
	f[x][1] += a[x];
}
int tim, id[maxn], dfn[maxn];

void dfs2(int x, int tp){
	dfn[x] = ++tim; id[tim] = x; top[x] = tp;
	ed[tp] = tim;
	if(son[x])dfs2(son[x], tp);
	val[x].a[1][0] = a[x];
	val[x].a[0][1] = 0;
	for(int i = head[x]; i; i = e[i].net){
		int v = e[i].to;
		if(v == son[x] || v == fa[x])continue;
		dfs2(v, v);
		val[x].a[0][1] += max(f[v][1], f[v][0]);
		val[x].a[1][0] += f[v][0];
	}
	val[x].a[0][0] = val[x].a[0][1];
}

struct seg{
	matrix t[maxn << 2 | 1];
	void push_up(int x){t[x] = t[x << 1] * t[x << 1 | 1];}
	void built(int x, int l, int r){
		if(l == r){t[x] = val[id[l]]; return;}
		int mid = (l + r) >> 1;
		built(x << 1, l, mid);
		built(x << 1 | 1, mid + 1, r);
		push_up(x);
	}
	matrix query(int x, int l, int r, int L, int R){
		if(L <= l && r <= R)return t[x];
		int mid = (l + r) >> 1;
		if(R <= mid)return query(x << 1, l, mid, L, R);
		if(L > mid)return query(x << 1 | 1, mid + 1, r, L, R);
		return query(x << 1, l, mid, L, R) * query(x << 1 | 1, mid + 1, r, L, R);
	}
	void modify(int x, int l, int r, int pos){
		if(l == r){
			t[x] = val[id[pos]];
			return;
		}
		int mid = (l + r) >> 1;
		if(pos <= mid)modify(x << 1, l, mid, pos);
		else modify(x << 1 | 1, mid + 1, r, pos);
		push_up(x);
	}
}t;

void upd(int x, int y){
	val[x].a[1][0] += y - a[x]; a[x] = y;
	matrix las, now;
	while(x){
		las = t.query(1, 1, n, dfn[top[x]], ed[top[x]]);
		t.modify(1, 1, n, dfn[x]);
		now = t.query(1, 1, n, dfn[top[x]], ed[top[x]]);
		x = fa[top[x]];
		val[x].a[0][0] += max(now.a[0][0], now.a[1][0]) - max(las.a[0][0], las.a[1][0]);
		val[x].a[0][1] = val[x].a[0][0];
		val[x].a[1][0] += now.a[0][0] - las.a[0][0];
	}
}

int main(){
	n = read(), m = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i < n; ++i){
		int u = read(), v = read();
		add(u, v); add(v, u);
	}
	dfs1(1); dfs2(1, 1);
	t.built(1, 1, n);
	for(int i = 1; i <= m; ++i){
		int x = read(), y = read();
		upd(x, y);
		matrix ans = t.query(1, 1, n, dfn[1], ed[1]);
		printf("%d\n",max(ans.a[0][0], ans.a[1][0]));
	}
	return 0;
}

最小权覆盖集 = 全集 最大权独立集

然后嘞?怎么处理制选和强制不选?

现在思考一下如何转化为上一个问题。

强制不选设为 inf 强制选设为 inf 并给全集加上 inf

然后就跟上个题一样了。就是常数大了点。

这个题其实可以用树上倍增的做法,其实也差不多。

我们的CSP题。。。

部分分看着拿。

k=1直接剖

k=2 不会到链外(图),于是如果把链抽出来就是

fi=wi+min(fi1,fi2)

不妨设 fi,0/1/2 表示走到距离点 i 距离为 0/1/2 的权值

上面的式子可以简单改写一下。

k=3

一定只会跳到邻接点(图)

预处理 gi 表示 i 邻接点最小权值

可以写出转移

fi,0=min(flas,0,flas,1,flas,2)+wi

fi,1=min(flas,0,flas,1+gi)

fi,2=flas,1

以上转化成广义矩阵乘法形式可以使用树剖等手法维护。

posted @   Chen_jr  阅读(62)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
历史上的今天:
2022-07-21 来自学长的馈赠2
2022-07-21 A层邀请赛1
点击右上角即可分享
微信分享提示