洛谷传送门
题目描述
给定一棵 个点的树,点带点权。
有 次操作,每次操作给定 ,表示修改点 的权值为 。
你需要在每次操作之后求出这棵树的最大权独立集的权值大小。
输入输出格式
输入格式:
第一行, ,分别代表点数和操作数。
第二行, ,代表 个点的权值。
接下来 行, ,描述这棵树的 条边。
接下来 行, ,修改点 的权值为 。
对于每个操作输出一行一个整数,代表这次操作后的树上最大权独立集。
保证答案在 范围内
输入输出样例
输入样例#1:
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
输出样例#1:
186
186
190
145
189
288
244
320
258
304
说明
对于 的数据,
对于 的数据,
对于 的数据,
解题分析
noip之前瞄了一眼这个玩意, 觉得太毒瘤了就没有细看, 然后居然考了QAQ…
如果不修改的话就是一个 。 然而如果加上了修改一下就变得毒瘤起来。
这里有一种很神奇的做法:将整颗树先进行树链剖分, 然后对于重链上的每个点, 维护其轻儿子上的信息, 再结合重链向上递推。
具体而言, 设
表示
序为
节点选与不选的子树的最优解,
表示选与不选虚儿子的贡献,
表示
的重儿子, 那么就有:
然后我们把矩阵乘法魔改一下, 把原来的乘法换成加法, 把原来的加法换成取
, 因为乘法和加法之间满足分配率, 加法满足交换律, 而加法和
之间也满足分配率,
满足交换律, 不难发现它们的关系是等价的, 所以改编出的矩乘也是满足结合律的。
构造出来的矩阵大概是这个样子的:
然后我们发现似乎对于一个重链末端的点, 第二个矩阵就是单位矩阵, 所以直接维护第一个矩阵的乘积就可以得到一个非末端的点的
值。 换句话说, 对于重链上编号为
(
是末端)的一部分,
可以通过这个后缀连乘积得到。
所以我们就可以用线段树维护区间矩阵乘积, 每次修改的时候单点修改到对应点的 值, 再查询链顶的 值来更新上一条重链对应的点。 总复杂度 。
代码如下:
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#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]));
}
}
写出了树剖的版本, 就不难写出 的版本了。 其实大致思想是一样的, 只是 要维护每个点的 值, 用其初始化当前点的矩阵。 的时候再向上更新 值就好了。
代码如下:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#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]));
}
}