线段树+树剖/\(lct\)维护广义矩阵乘法
从例题开始讲
如果不带修改,那么就好做了
\(f_{i, 1 / 0}\) 表示 \(i\) 节点选或不选的最大权
容易得到转移
\[ f_{i, 0} = \sum_{son} max(f_{son, 0}, f_{son, 1}) \]\[ f_{i, 1} = w_i+\sum_{son} f_{son, 0} \]但是现在带修。
你会发现,一次修改只会影响一条链上的值,但不幸的是暴力修改仍是不可接受的
有什么方法可以快速处理树链?树剖,(LCT)
现在的问题是如何快速维护一条树链的 \(DP\) 值
我们先引入 广义矩阵乘法
那么这根我们的 \(DP\) 有什么关系呢?
我们尝试将 \(DP\) 写成矩阵的形式
为了保证复杂度,我们定义 \(g_{i, 0/1}\) 表示只考虑 \(i\) 的轻儿子, \(i\) 选或不选的最大值
那么有 (\(son\) 为重儿子)
\[ f_{i, 0} = g_{i, 0} + max(f_{son, 1}, f_{son, 0}) \]\[ f_{i, 1} = g_{i,1} + f_{son, 0} \]把他写成广义矩阵
\[ \begin{bmatrix} g_{i,0} & g_{i,0}\\ g_{i,1} & -\infty \end{bmatrix}\times \begin{bmatrix} f_{son,0}\\f_{son,1} \end{bmatrix}= \begin{bmatrix} f_{i,0}\\f_{i,1} \end{bmatrix} \]使用树剖+线段树维护区间转移矩阵,就能快速转移一条重链,而修改只需修改他的 \(g_{i, 1}\)和向上的一条链
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\) 不会到链外(图),于是如果把链抽出来就是
\(f_{i} = w_i + min(f_{i - 1}, f_{i - 2})\)
不妨设 \(f_{i, 0 / 1 / 2}\) 表示走到距离点 \(i\) 距离为 \(0 / 1 / 2\) 的权值
上面的式子可以简单改写一下。
\(k = 3\)
一定只会跳到邻接点(图)
预处理 \(g_i\) 表示 \(i\) 邻接点最小权值
可以写出转移
\[ f_{i, 0} = min(f_{las, 0}, f_{las, 1}, f_{las, 2}) + w_i \]\[ f_{i, 1} = min(f_{las, 0}, f_{las, 1} + g_i) \]\[ f_{i, 2} = f_{las, 1} \]以上转化成广义矩阵乘法形式可以使用树剖等手法维护。
标签:matrix,val,int,DDP,son,maxn,bmatrix From: https://www.cnblogs.com/Chencgy/p/18314293