DDP
线段树+树剖/维护广义矩阵乘法
从例题开始讲
如果不带修改,那么就好做了
表示 节点选或不选的最大权
容易得到转移
但是现在带修。
你会发现,一次修改只会影响一条链上的值,但不幸的是暴力修改仍是不可接受的
有什么方法可以快速处理树链?树剖,(LCT)
现在的问题是如何快速维护一条树链的 值
我们先引入 广义矩阵乘法
那么这根我们的 有什么关系呢?
我们尝试将 写成矩阵的形式
为了保证复杂度,我们定义 表示只考虑 的轻儿子, 选或不选的最大值
那么有 ( 为重儿子)
把他写成广义矩阵
使用树剖+线段树维护区间转移矩阵,就能快速转移一条重链,而修改只需修改他的 和向上的一条链
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;
}
最小权覆盖集 全集 最大权独立集
然后嘞?怎么处理制选和强制不选?
现在思考一下如何转化为上一个问题。
强制不选设为 强制选设为 并给全集加上
然后就跟上个题一样了。就是常数大了点。
这个题其实可以用树上倍增的做法,其实也差不多。
我们的题。。。
部分分看着拿。
直接剖
不会到链外(图),于是如果把链抽出来就是
不妨设 表示走到距离点 距离为 的权值
上面的式子可以简单改写一下。
一定只会跳到邻接点(图)
预处理 表示 邻接点最小权值
可以写出转移
以上转化成广义矩阵乘法形式可以使用树剖等手法维护。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2022-07-21 来自学长的馈赠2
2022-07-21 A层邀请赛1