Problem
-
弱化版动态询问一棵树的最小覆盖集.
-
每次只选择其中某两个点必选或必不选,且询问独立.
Data constraint
Solution
【动态DP = 树链剖分 + 线段树 + 矩阵转移】
-
这好像是 搞出来的一个新玩意儿,挺神奇的.
-
首先,最小覆盖集 = 全集 - 最大独立集.
-
其次,一个点必选或不必选,可以直接把他的权值赋值为无穷大,或无穷小.
-
这样,问题完美转化为求解一棵树的最大独立集.
-
一个小学四年级的DP是 表示 点选或不选,最大收益.
-
先树剖一下,然后一个初三的DP是,设一个 表示与 含义相同,唯独不能用重儿子去转移.
-
这样有什么好处?
-
我们不妨用 表示当前要计算 的点,假设它在线段树上的编号也是 ,那么它的重儿子在线段树上的编号就是 .
-
那么你可以写出这样的两条式子: .
-
然后神奇的一步出现,你考虑把它写成矩阵转移的形式,会变成:
-
这里需要注意,普通的矩阵乘法形式是形如:
-
而这里,我们把它改变一下,变成
-
并且发现后者也是满足所谓的结合律的.
-
那么不难发现,其实一个点的答案,就是它所在重链的每一个节点的g矩阵乘起来.
-
因为满足结合律,所以刚好可以用链剖+线段树来维护.
-
然后考虑修改,假设修改一个点 的权值,那么其实父亲的 矩阵不会改变,I.e.其所在重链的所有祖先的 矩阵不会改变.
-
会改变的仅仅是 本身的 矩阵,以及所在重链每一个祖先的 值(答案).
-
因为会影响祖先的 值,所以在经过完这条重链后走一条轻边到达另一条重链时,会影响到那条链上的 矩阵.
-
所以我们的修改步骤就很明确了:
-
先修改当前权值,然后处理一下 矩阵.
-
然后进入循环,一直向顶端去修改.
-
-
具体实现的时候,实际上我们可以记录一个 表示 这个点在线段树上的编号.
-
那么修改一个点的 矩阵直接修改即可,然后暴力的往上跳,进行合并即可.
-
这样常数会小很多,且可以优化一下这个转移,把它不要真的写成取max,这样太慢.
-
参考代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#define ll long long
#define F(i, a, b) for (ll i = a; i <= b; i ++)
#define max(a, b) ((a) > (b) ? (a) : (b))
#define mem(a, b) memset(a, b, sizeof a)
#define mec(a, b) memcpy(a, b, sizeof a)
#define mx(a, b) ((a) = max(a, b))
#define get getchar()
#define M (st + en >> 1)
#define Ls (x << 1)
#define Rs (Ls | 1)
const ll N = 2e5 + 10, T = 4 * N, W = 1e15;
using namespace std;
ll n, m, u, v, a, x, b, y, t, cnt, Sum, sum, DFN[N], V[N]; char ch[10];
ll sz[N], fa[N], Son[N], top[N], dfn[N], f[N][2], B[N], D[N];
ll tov[T], nex[T], las[N], tot, S0, S1;
struct mat {
ll g[2][2];
mat() { mem(g, 0); }
inline mat operator * (const mat &b) const { mat c;
c.g[0][0] = max(g[0][0] + b.g[0][0], g[0][1] + b.g[1][0]);
c.g[0][1] = max(g[0][0] + b.g[0][1], g[0][1] + b.g[1][1]);
c.g[1][0] = max(g[1][0] + b.g[0][0], g[1][1] + b.g[1][0]);
c.g[1][1] = max(g[1][0] + b.g[0][1], g[1][1] + b.g[1][1]);
return c;
}
} tr[T], TR[T], ANS, LAS, NOW, PRE, SON;
inline void Re(ll &x) {
char c = get; x = 0; ll t = 1;
for (; !isdigit(c); c = get) t = (c == '-' ? - 1 : t);
for (; isdigit(c); x = (x << 3) + (x << 1) + c - '0', c = get); x *= t;
}
inline void Wr(ll x) {
if (x < 0) { putchar('-'); x = - x; }
if (x > 9) Wr(x / 10); putchar(x % 10 + '0');
}
void ins(ll x, ll y) { tov[++ tot] = y, nex[tot] = las[x], las[x] = tot;}
void Dfs(ll k) {
sz[k] ++;
for (ll x= las[k], Hv = 0; x ; x = nex[x])
if (!sz[tov[x]]) fa[tov[x]] = k, Dfs(tov[x]), Son[k] = sz[tov[x]] > Hv ? tov[x] : Son[k], mx(Hv, sz[tov[x]]), sz[k] += sz[tov[x]];
}
void Dfn(ll k) {
dfn[k] = DFN[k] = ++ cnt;
if (Son[k]) top[Son[k]] = top[k], Dfn(Son[k]), DFN[k] = DFN[Son[k]];
for (ll x = las[k] ; x ; x = nex[x])
if (tov[x] ^ Son[k] && sz[tov[x]] < sz[k])
top[tov[x]] = tov[x], Dfn(tov[x]);
}
void Modify(ll x, ll st, ll en, ll p) {
if (st == en) B[st] = x;
else
M >= dfn[p] ? Modify(Ls, st, M, p) : Modify(Rs, M + 1, en, p);
}
void Down(int x) { for (; x > 1 ; x >>= 1, tr[x] = tr[Ls] * tr[Rs]); }
void DP(ll k) {
for (ll x = las[k] ; x ; x = nex[x])
if (sz[k] > sz[tov[x]]) DP(tov[x]);
for (ll x = las[k], y; x ; x = nex[x])
if (sz[k] > sz[y = tov[x]]) {
f[k][0] += max(f[y][0], f[y][1]);
f[k][1] += f[y][0];
}
f[k][1] += V[k];
tr[B[dfn[k]]].g[0][0] = tr[B[dfn[k]]].g[0][1] = f[k][0] - max(f[Son[k]][0], f[Son[k]][1]);
tr[B[dfn[k]]].g[1][0] = f[k][1] - f[Son[k]][0], tr[B[dfn[k]]].g[1][1] = - W;
Down(B[dfn[k]]);
}
mat Query(ll x, ll st, ll en, ll l, ll r) {
if (l <= st && en <= r) return tr[x];
if (r <= M) return Query(Ls, st, M, l, r);
if (l > M) return Query(Rs, M + 1, en, l, r);
return Query(Ls, st, M, l, r) * Query(Rs, M + 1, en, l, r);
}
void Update(ll x, ll y) {
LAS = Query(1, 1, n, dfn[top[x]], DFN[top[x]]), tr[B[dfn[x]]].g[1][0] += y;
Down(B[dfn[x]]), D[++ D[0]] = x;
while (fa[top[x]]) {
ANS = Query(1, 1, n, dfn[top[x]], DFN[top[x]]); t = top[x], x = fa[t];
if (top[x]) PRE = Query(1, 1, n, dfn[top[x]], DFN[top[x]]);
NOW = Query(1, 1, n, dfn[x], DFN[x]);
SON = Query(1, 1, n, dfn[Son[x]], DFN[Son[x]]);
tr[B[dfn[x]]].g[0][0] = tr[B[dfn[x]]].g[0][1] =
NOW.g[0][0] - max(SON.g[0][0], SON.g[1][0]) - max(LAS.g[0][0], LAS.g[1][0]) + max(ANS.g[0][0], ANS.g[1][0]);
tr[B[dfn[x]]].g[1][0] =
NOW.g[1][0] - SON.g[0][0] - LAS.g[0][0] + ANS.g[0][0];
Down(B[dfn[x]]), LAS = PRE, D[++ D[0]] = x;
}
}
int main() {
freopen("defense.in", "r", stdin);
freopen("defense.out", "w", stdout);
Re(n), Re(m); scanf("%s", ch + 1);
F(i, 1, n) Re(V[i]), sum += V[i];
F(i, 1, n - 1) Re(u), Re(v), ins(u, v), ins(v, u);
Dfs(1), top[1] = 1, Dfn(1);
F(i, 1, n) Modify(1, 1, n, i);
DP(1), mec(TR, tr);
F(i, 1, m) {
Re(a), Re(x), Re(b), Re(y), Sum = 0, D[0] = 0;
if ((fa[a] == b || fa[b] == a) && x + y == 0) { puts("-1"); continue; }
if (x == 0) Update(a, W), Sum += W; else Update(a, - W);
if (y == 0) Update(b, W), Sum += W; else Update(b, - W);
ANS = Query(1, 1, n, 1, DFN[1]), Wr(sum - (max(ANS.g[0][0], ANS.g[1][0]) - Sum)), putchar('\n');
F(i, 1, D[0]) tr[B[dfn[D[i]]]] = TR[B[dfn[D[i]]]], Down(B[dfn[D[i]]]);
}
}
【倍增DP】
- 待学