给定一棵 n n n个点的树,点带点权。
有 m m m次操作,每次操作给定 x , y x,y x,y,表示修改点 x x x的权值为 y y y。
你需要在每次操作之后求出这棵树的最大权独立集的权值大小。
第一行, n , m n,m n,m,分别代表点数和操作数。
第二行, V 1 , V 2 , . . . , V n V_1,V_2,...,V_n V1,V2,...,Vn,代表 n n n个点的权值。
接下来 n − 1 n-1 n−1行, x , y x,y x,y,描述这棵树的 n − 1 n-1 n−1条边。
接下来 m m m行, x , y x,y x,y,修改点 x x x的权值为 y y y。
对于每个操作输出一行一个整数,代表这次操作后的树上最大权独立集。
保证答案在 i n t int int范围内
10 10
-11 80 -99 -76 56 38 92 -51 -34 47
2 1
3 1
4 3
5 2
6 2
7 1
8 2
9 4
10 7
9 -44
2 -17
2 98
7 -58
8 48
3 99
8 -61
9 76
9 14
10 93
186
186
190
145
189
288
244
320
258
304
对于 30 % 30\% 30%的数据, 1 ≤ n , m ≤ 10 1\le n,m\le 10 1≤n,m≤10
对于 60 % 60\% 60%的数据, 1 ≤ n , m ≤ 1000 1\le n,m\le 1000 1≤n,m≤1000
对于 100 % 100\% 100%的数据, 1 ≤ n , m ≤ 1 0 5 1\le n,m\le 10^5 1≤n,m≤105
noip之前瞄了一眼这个玩意, 觉得太毒瘤了就没有细看, 然后居然考了QAQ…
如果不修改的话就是一个 S B 0 / 1 D P SB0/1DP SB0/1DP。 然而如果加上了修改一下就变得毒瘤起来。
这里有一种很神奇的做法:将整颗树先进行树链剖分, 然后对于重链上的每个点, 维护其轻儿子上的信息, 再结合重链向上递推。
具体而言, 设 d p [ i ] [ 1 / 0 ] dp[i][1/0] dp[i][1/0]表示 D F S DFS DFS序为 i i i节点选与不选的子树的最优解, g [ i ] [ 1 / 0 ] g[i][1/0] g[i][1/0]表示选与不选虚儿子的贡献, s o n [ i ] son[i] son[i]表示 i i i的重儿子, 那么就有:
g [ i ] [ 0 ] = ∑ E d g e ( i , j ) , j ≠ s o n [ i ] m a x ( d p [ j ] [ 0 ] , d p [ j ] [ 1 ] ) g [ i ] [ 1 ] = ∑ E d g e ( i , j ) , j ≠ s o n [ i ] d p [ j ] [ 0 ] + v a l [ i ] d p [ i ] [ 0 ] = g [ i ] [ 0 ] + m a x ( d p [ s o n [ i ] ] [ 0 ] , d p [ s o n [ i ] ] [ 1 ] ) d p [ i ] [ 1 ] = g [ i ] [ 1 ] + d p [ s o n [ i ] [ 0 ] ] g[i][0]=\sum_{Edge(i,j),j\ne son[i]} max(dp[j][0],dp[j][1]) \\ g[i][1] =\sum_{Edge(i,j),j\ne son[i]}dp[j][0] +val[i] \\ dp[i][0]=g[i][0]+max(dp[son[i]][0],dp[son[i]][1]) \\ dp[i][1]=g[i][1]+dp[son[i][0]] g[i][0]=Edge(i,j),j̸=son[i]∑max(dp[j][0],dp[j][1])g[i][1]=Edge(i,j),j̸=son[i]∑dp[j][0]+val[i]dp[i][0]=g[i][0]+max(dp[son[i]][0],dp[son[i]][1])dp[i][1]=g[i][1]+dp[son[i][0]]
然后我们把矩阵乘法魔改一下, 把原来的乘法换成加法, 把原来的加法换成取 m a x max max, 因为乘法和加法之间满足分配率, 加法满足交换律, 而加法和 m a x max max之间也满足分配率, m a x max max满足交换律, 不难发现它们的关系是等价的, 所以改编出的矩乘也是满足结合律的。
构造出来的矩阵大概是这个样子的:
[ g [ i ] [ 0 ] g [ i ] [ 0 ] g [ i ] [ 1 ] − I N F ] × [ d p [ s o n [ i ] ] [ 0 ] d p [ s o n [ i ] ] [ 1 ] ] = [ d p [ i ] [ 0 ] d p [ i ] [ 1 ] ] \left[ \begin{matrix} g[i][0] & g[i][0]\\ g[i][1] & -INF \end{matrix} \right] \times \left[ \begin{matrix} dp[son[i]][0]\\ dp[son[i]][1] \end{matrix} \right] = \left[ \begin{matrix} dp[i][0]\\ dp[i][1] \end{matrix} \right] [g[i][0]g[i][1]g[i][0]−INF]×[dp[son[i]][0]dp[son[i]][1]]=[dp[i][0]dp[i][1]]
然后我们发现似乎对于一个重链末端的点, 第二个矩阵就是单位矩阵, 所以直接维护第一个矩阵的乘积就可以得到一个非末端的点的 d p dp dp值。 换句话说, 对于重链上编号为 i , i + 1 , . . . , j i,i+1,...,j i,i+1,...,j( j j j是末端)的一部分, d p [ i ] dp[i] dp[i]可以通过这个后缀连乘积得到。
所以我们就可以用线段树维护区间矩阵乘积, 每次修改的时候单点修改到对应点的 g [ 1 ] g[1] g[1]值, 再查询链顶的 d p dp dp值来更新上一条重链对应的点。 总复杂度 O ( 8 × n l o g 2 ( n ) ) O(8\times nlog^2(n)) O(8×nlog2(n))。
代码如下:
#include
#include
#include
#include
#include
#include
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 100500
#define ll long long
#define INF 1000000000
bool neg;
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc)
if (c == '-') neg = true;
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
if (neg) neg = false, x = -x;
}
template <class T> IN T max(T a, T b) {return a > b ? a : b;}
template <class T> IN T min(T a, T b) {return a < b ? a : b;}
int head[MX], son[MX], siz[MX], fat[MX], top[MX], topf[MX], dfn[MX], end[MX], ind[MX];
ll dp[MX][2], val[MX];
int dot, q, ct, cnt;
struct Edge {int to, nex;} edge[MX << 1];
IN void add(R int from, R int to) {edge[++cnt] = {to, head[from]}, head[from] = cnt;}
struct Matrix
{
ll mat[2][2];
Matrix (){mat[0][0] = mat[0][1] = mat[1][0] = mat[1][1] = -INF;}
}tree[MX << 2], bf[MX], unit;
IN Matrix operator + (const Matrix &x, const Matrix &y)
{
Matrix ret;
for (R int i = 0; i < 2; ++i)
for (R int j = 0; j < 2; ++j)
ret.mat[i][j] = x.mat[i][j] + y.mat[i][j];
return ret;
}
IN Matrix operator * (const Matrix &x, const Matrix &y)
{
Matrix ret;
ret.mat[0][0] = max(x.mat[0][0] + y.mat[0][0], x.mat[0][1] + y.mat[1][0]);
ret.mat[0][1] = max(x.mat[0][0] + y.mat[0][1], x.mat[0][1] + y.mat[1][1]);
ret.mat[1][0] = max(x.mat[1][0] + y.mat[0][0], x.mat[1][1] + y.mat[1][0]);
ret.mat[1][1] = max(x.mat[1][0] + y.mat[0][1], x.mat[1][1] + y.mat[1][1]);
return ret;
}
void DP(R int now)
{
dp[now][1] = val[now];
for (R int i = head[now]; i; i = edge[i].nex)
{
if (edge[i].to == fat[now]) continue;
DP(edge[i].to);
dp[now][0] += max(dp[edge[i].to][1], dp[edge[i].to][0]);
dp[now][1] += dp[edge[i].to][0];
}
}
namespace HLD
{
#define ls (now << 1)
#define rs (now << 1 | 1)
void DFS(R int now)
{
siz[now] = 1;
for (R int i = head[now]; i; i = edge[i].nex)
{
if (edge[i].to == fat[now]) continue;
fat[edge[i].to] = now;
DFS(edge[i].to); siz[now] += siz[edge[i].to];
if (siz[edge[i].to] > siz[son[now]]) son[now] = edge[i].to;
}
}
void DFS(R int now, R int grand)
{
dfn[now] = ++ct; ind[ct] = now;
topf[now] = grand;
if (!son[now]) return end[topf[now]] = dfn[now], void();
DFS(son[now], grand);
for (R int i = head[now]; i; i = edge[i].nex)
{
if (edge[i].to == fat[now] || edge[i].to == son[now]) continue;
DFS(edge[i].to, edge[i].to);
}
}
IN void pushup(R int now) {tree[now] = tree[ls] * tree[rs];}
void build(R int now, R int lef, R int rig)
{
if (lef == rig)
{
R int cur = ind[lef];
ll g0 = 0, g1 = val[cur];
for (R int i = head[cur]; i; i = edge[i].nex)
{
if (edge[i].to != son[cur] && edge[i].to != fat[cur])
{
g0 += max(dp[edge[i].to][0], dp[edge[i].to][1]);
g1 += dp[edge[i].to][0];
}
}
tree[now].mat[0][0] = tree[now].mat[0][1] = g0;
tree[now].mat[1][0] = g1; tree[now].mat[1][1] = -INF;
bf[lef] = tree[now];
return;
}
int mid = lef + rig >> 1;
build(ls, lef, mid), build(rs, mid + 1, rig);
pushup(now);
}
void modify(R int now, R int lef, R int rig, R int tar)
{
if (lef == rig) return tree[now] = bf[lef], void();
int mid = lef + rig >> 1;
if (tar <= mid) modify(ls, lef, mid, tar);
else modify(rs, mid + 1, rig, tar);
pushup(now);
}
Matrix query(R int now, R int lef, R int rig, R int lb, R int rb)
{
if (lef >= lb && rig <= rb) return tree[now];
int mid = lef + rig >> 1; Matrix ret = unit;
if (lb <= mid && rb > mid) return query(ls, lef, mid, lb, rb) * query(rs, mid + 1, rig, lb, rb);
if (lb <= mid) return query(ls, lef, mid, lb, rb);
return query(rs, mid + 1, rig, lb, rb);
}
Matrix get_tp(R int id) {return query(1, 1, dot, dfn[id], end[id]);}//顶端的dp值
IN void modify(R int now, ll del)
{
bf[dfn[now]].mat[1][0] += del - val[now], val[now] = del;
Matrix pr, nw;
W (now)
{
pr = get_tp(topf[now]);
modify(1, 1, dot, dfn[now]);
nw = get_tp(topf[now]);
now = fat[topf[now]]; if (!now) break;
bf[dfn[now]].mat[0][0] += max(nw.mat[0][0], nw.mat[1][0]) - max(pr.mat[0][0], pr.mat[1][0]);
bf[dfn[now]].mat[0][1] = bf[dfn[now]].mat[0][0];
bf[dfn[now]].mat[1][0] += nw.mat[0][0] - pr.mat[0][0];
}
}
#undef ls
#undef rs
}
int main(void)
{
int a, b, tar; ll buf;
in(dot), in(q); unit.mat[0][0] = unit.mat[1][1] = 0;
for (R int i = 1; i <= dot; ++i) in(val[i]);
for (R int i = 1; i < dot; ++i) in(a), in(b), add(a, b), add(b, a);
HLD::DFS(1); HLD::DFS(1, 1); DP(1); HLD::build(1, 1, dot);
Matrix ret;
W (q--)
{
in(tar), in(buf);
HLD::modify(tar, buf);
ret = HLD::get_tp(1);
printf("%lld\n", max(ret.mat[0][0], ret.mat[1][0]));
}
}
写出了树剖的版本, 就不难写出 L C T LCT LCT的版本了。 其实大致思想是一样的, 只是 L C T LCT LCT要维护每个点的 g g g值, 用其初始化当前点的矩阵。 a c c e s s access access的时候再向上更新 g g g值就好了。
代码如下:
#include
#include
#include
#include
#include
#include
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 100500
#define INF 1000000000
#define ll long long
bool neg;
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc)
if (c == '-') neg = true;
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
if (neg) neg = false, x = -x;
}
template <class T> IN T max(T a, T b) {return a > b ? a : b;}
template <class T> IN T min(T a, T b) {return a < b ? a : b;}
int head[MX], val[MX], fat[MX], sta[MX];
ll dp[MX][2];
int dot, q, cnt, top;
struct Edge {int to, nex;} edge[MX << 1];
IN void add(R int from, R int to) {edge[++cnt] = {to, head[from]}, head[from] = cnt;}
struct Matrix
{
ll mat[2][2];
Matrix(){mat[0][0] = mat[0][1] = mat[1][0] = mat[1][1] = -INF;}
IN void ini(ll a, ll b) {mat[0][0] = mat[0][1] = a, mat[1][0] = b, mat[1][1] = -INF;}
};
IN Matrix operator * (const Matrix &x, const Matrix &y)
{
Matrix ret;
ret.mat[0][0] = max(x.mat[0][0] + y.mat[0][0], x.mat[0][1] + y.mat[1][0]);
ret.mat[0][1] = max(x.mat[0][0] + y.mat[0][1], x.mat[0][1] + y.mat[1][1]);
ret.mat[1][0] = max(x.mat[1][0] + y.mat[0][0], x.mat[1][1] + y.mat[1][0]);
ret.mat[1][1] = max(x.mat[1][0] + y.mat[0][1], x.mat[1][1] + y.mat[1][1]);
return ret;
}
struct Node {int son[2], fat; ll f[2]; Matrix dp; bool rev;} tree[MX];
namespace LCT
{
#define ls tree[now].son[0]
#define rs tree[now].son[1]
#define dad tree[now].fat
IN bool get(R int now) {return tree[dad].son[1] == now;}
IN bool nroot(R int now) {return tree[dad].son[0] == now || tree[dad].son[1] == now;}
IN void pushup(R int now)
{
tree[now].dp.ini(tree[now].f[0], tree[now].f[1]);
if (ls) tree[now].dp = tree[ls].dp * tree[now].dp;
if (rs) tree[now].dp = tree[now].dp * tree[rs].dp;
}
IN void pushrev(R int now) {std::swap(ls, rs), tree[now].rev ^= 1;}
IN void pushdown(R int now) {if (tree[now].rev) pushrev(ls), pushrev(rs), tree[now].rev = false;}
IN void rotate(R int now)
{
R int fa = dad, grand = tree[fa].fat;
R bool dir = get(now);
tree[fa].son[dir] = tree[now].son[dir ^ 1];
tree[tree[now].son[dir ^ 1]].fat = fa;
tree[now].fat = grand;
if (nroot(fa)) tree[grand].son[get(fa)] = now;
tree[fa].fat = now;
tree[now].son[dir ^ 1] = fa;
pushup(fa); pushup(now);
}
IN void splay(R int now)
{
R int tmp = now, fa;
sta[top = 1] = now;
W (nroot(now)) sta[++top] = now = dad;
W (top) pushdown(sta[top--]);
now = tmp;
W (nroot(now))
{
fa = dad;
if (nroot(fa)) rotate(get(now) == get(fa) ? fa : now);
rotate(now);
}
pushup(now);
}
IN void access(R int now)
{
for (R int x = 0; now; x = now, now = dad)
{
splay(now);
if (rs)
{
tree[now].f[0] += max(tree[rs].dp.mat[0][0], tree[rs].dp.mat[1][0]);
tree[now].f[1] += tree[rs].dp.mat[0][0];
}
if (x)
{
tree[now].f[0] -= max(tree[x].dp.mat[0][0], tree[x].dp.mat[1][0]);
tree[now].f[1] -= tree[x].dp.mat[0][0];
}
rs = x; pushup(now);
}
}
IN void makeroot(R int now) {access(now), splay(now), pushrev(now);}
#undef ls
#undef rs
#undef dad
}
void DFS(R int now, R int fa)
{
dp[now][1] = val[now];
for (R int i = head[now]; i; i = edge[i].nex)
{
if (edge[i].to == fa) continue;
tree[edge[i].to].fat = now;
DFS(edge[i].to, now);
dp[now][1] += dp[edge[i].to][0];
dp[now][0] += max(dp[edge[i].to][0], dp[edge[i].to][1]);
}
tree[now].dp.ini(dp[now][0], dp[now][1]);
tree[now].f[0] = dp[now][0], tree[now].f[1] = dp[now][1];
}
int main(void)
{
int a, b, tar;
ll buf;
in(dot), in(q);
for (R int i = 1; i <= dot; ++i) in(val[i]);
for (R int i = 1; i < dot; ++i) in(a), in(b), add(a, b), add(b, a);
DFS(1, 0);
W (q--)
{
in(tar), in(buf);
LCT::access(tar), LCT::splay(tar);
tree[tar].f[1] += buf - val[tar]; val[tar] = buf;
LCT::pushup(tar); LCT::splay(1);
printf("%lld\n", max(tree[1].dp.mat[0][0], tree[1].dp.mat[1][0]));
}
}