题目大意

这还不是人尽皆知?

有一棵树, 每个节点放军队的代价是\(a_i\), 一条边连接的两个点至少有一个要放军队, 还有\(q\)次询问, 每次规定其中的两个一定需要/不可放置军队, 问这样修改以后的最小代价.

解题思路

考虑一个朴素的DP, 设\(f_{x,0/1}\)表示这个点选/不选的最小代价. 显然有

\[f_{x,0}=\sum f_{y,1}
\]
\[f_{x,1}=a_x+\sum \min(f_{y,0}, f_{y,1})
\]

其中\(y\)是\(x\)的儿子. 最后的答案显然是\(min(f_{1,0}, f_{1,1})\)(1是根)

注意到我们每一次修改以后, 影响到的是从这个点到根节点的一条链. 我们需要快速修改这一段的\(f\)值. 怎么样可以使这个操作的时间复杂度变得正确一点呢? 显然树剖吧.

那就剖咯. 对轻重儿子区别对待. 设\(g_{x,0/1}\)表示\(x\)这个点选/不选, 除了重儿子外的最小代价. 显然有

\[g_{x,0}=\sum_{y \ne son_x} f_{y,1}
\]
\[g_{x,1}=a_x+\sum_{y \ne son_x} \min(f_{y,0}, f_{y,1})
\]

只不过是少了一个重儿子而已

那么\(f\)可以借助\(g\)转移过来.

\[f_{x,0}=f_{son,1}+g_{x,0}
\]
\[f_{x,1}=\min(f_{son,0}, f_{son,1})+g_{x,1}
\]

问题来了. 怎么维护一条链的信息?

考虑重新定义矩阵乘法, 若\(AB=C\), 则

\[C_{i,j}=\min_{k}(A_{i,k}+B_{k,j})
\]

于是转移方程可以写成如下

\[\begin{bmatrix}
+\infty & g_{x,0}\\
g_{x,1} & g_{x,1}
\end{bmatrix}

\begin{bmatrix}
f_{son,0}\\
f_{son,1}
\end{bmatrix}

=

\begin{bmatrix}
f_{x,0} \\
f_{x,1}
\end{bmatrix}
\]

在每一条链上维护\(g\)所在矩阵的乘积. 把一个询问按照重链分解成几条从当前点到根节点的链即可. 考虑当我们限制一个点强制选/不选后, 只需要把\(g\)数组对应的值赋成\(+\infty\)即可.

后记

其实一次有两个被限制了, 就是在暗示我们除了动态DP还有更简单的联赛做法.

但是我太懒了, 而且没做过树链剖分的题, 所以试一下(没错蒟蒻的第一个树链剖分就是做动态DP)

(可能是本人写过最长的Code?)

#include <cstdio>
#include <cstring>
#define N 100010
#define ll long long
#define INF 0x3f3f3f3f3f3f3f3fll
#define init(a, b) memset(a, b, sizeof(a))
#define fo(i, a, b) for(int i = (a); i <= (b); ++i)
#define fd(i, a, b) for(int i = (a); i >= (b); --i)
//#pragma GCC optimize(2)
using namespace std;
inline int read() // notice : 1. long long ? 2. negative ?
{
int x = 0; char ch = getchar();
while(ch < '0' || ch > '9') ch = getchar();
while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
return x;
}
inline ll min(ll a, ll b){return a < b ? a : b;}
struct Matrix
{
ll a[2][2];
inline Matrix(){init(a, 0x3f);}
inline Matrix(int){a[0][0] = a[1][1] = 0; a[0][1] = a[1][0] = INF;}
inline Matrix(ll g0, ll g1){a[0][0] = INF, a[0][1] = g0, a[1][0] = a[1][1] = g1;}
inline Matrix operator*(const Matrix &b)
{
Matrix ret;
fo(i, 0, 1) fo(j, 0, 1) fo(k, 0, 1) ret.a[i][j] = min(ret.a[i][j], a[i][k] + b.a[k][j]);
return ret;
}
}tr[N << 2];
int n, q, ls[N << 2], rs[N << 2], rt[N], top[N], fa[N], sz[N], son[N], dfn[N], end[N], a[N], to[N << 1], pre[N << 1], last[N];
ll f[N][2], g[N][2];
#define mid ((l + r) >> 1)
void build(int &t, int l, int r)
{
static int tot = 0;
!t && (t = ++tot);
if(l == r) return ;
build(ls[t], l, mid); build(rs[t], mid + 1, r);
}
void insert(int t, int l, int r, Matrix v, int w)
{
if(l == r) return (void)(tr[t] = v, 0);
w <= mid ? insert(ls[t], l, mid, v, w) : insert(rs[t], mid + 1, r, v, w);
tr[t] = tr[ls[t]] * tr[rs[t]];
}
Matrix query(int t, int l, int r, int fl, int fr)
{
if(fl <= l && r <= fr) return tr[t];
Matrix ret(1);
fl <= mid && (ret = ret * query(ls[t], l, mid, fl, fr), 1);
fr > mid && (ret = ret * query(rs[t], mid + 1, r, fl, fr), 1);
return ret;
} inline void add(int u, int v){static int tot = 0; to[++tot] = v, pre[tot] = last[u], last[u] = tot;}
inline void dfs1(int u)
{
sz[u] = 1, f[u][1] = a[u];
for(int i = last[u]; i; i = pre[i])
if(to[i] ^ fa[u])
{
int v = to[i];
fa[v] = u; dfs1(v), sz[u] += sz[v];
f[u][0] += f[v][1], f[u][1] += min(f[v][0], f[v][1]);
sz[v] > sz[son[u]] && (son[u] = v);
}
}
inline void dfs2(int u)
{
static int tot = 0;
dfn[u] = ++tot, g[u][1] = a[u];
if(son[u]) top[son[u]] = top[u], dfs2(son[u]), end[u] = end[son[u]];
else end[u] = u;
for(int i = last[u]; i; i = pre[i])
if(to[i] ^ fa[u] && to[i] ^ son[u])
{
int v = to[i];
top[v] = v;
g[u][0] += f[v][1], g[u][1] += min(f[v][0], f[v][1]);
dfs2(v);
}
}
inline void update(int x)
{
for(; x; x = fa[x])
{
insert(rt[top[x]], dfn[top[x]], dfn[end[x]], Matrix(g[x][0], g[x][1]), dfn[x]);
Matrix p = tr[rt[x = top[x]]];
// printf("f[%d] : %lld %lld\n", x, f[x][0], f[x][1]);
g[fa[x]][0] -= f[x][1], g[fa[x]][1] -= min(f[x][0], f[x][1]);
f[x][0] = min(p.a[0][0], p.a[0][1]), f[x][1] = min(p.a[1][0], p.a[1][1]);
g[fa[x]][0] += f[x][1], g[fa[x]][1] += min(f[x][0], f[x][1]);
}
}
int main()
{
freopen("defense.in", "r", stdin);
freopen("defense.out", "w", stdout);
n = read(), q = read(); scanf(" %*s");
fo(i, 1, n) a[i] = read();
int u, v, x, y; ll t1, t2;
fo(i, 2, n) u = read(), v = read(), add(u, v), add(v, u);
top[1] = 1;
dfs1(1);
dfs2(1);
fo(i, 1, n) if(top[i] == i) build(rt[i], dfn[i], dfn[end[i]]);
fo(i, 1, n) insert(rt[top[i]], dfn[top[i]], dfn[end[i]], Matrix(g[i][0], g[i][1]), dfn[i]);
fo(i, 1, q)
{
u = read(), x = read() ^ 1, v = read(), y = read() ^ 1;
t1 = g[u][x], t2 = g[v][y];
g[u][x] = INF, update(u), g[v][y] = INF, update(v);
ll ans = min(f[1][0], f[1][1]);
printf("%lld\n", ans < INF ? ans : -1);
g[u][x] = t1, update(u), g[v][y] = t2, update(v); }
return 0;
}

JZOJ5966. [NOIP2018TGD2T3] 保卫王国 (动态DP做法)的更多相关文章

  1. luogu5024 [NOIp2018]保卫王国 (动态dp)

    可以直接套动态dp,但因为它询问之间相互独立,所以可以直接倍增记x转移到fa[x]的矩阵 #include<bits/stdc++.h> #define CLR(a,x) memset(a ...

  2. 【NOIP2018】保卫王国 动态dp

    此题场上打了一个正确的$44pts$,接着看错题疯狂$rush$“正确”的$44pts$,后来没$rush$完没将之前的代码$copy$回去,直接变零分了..... 这一题我们显然有一种$O(nm)$ ...

  3. luoguP5024 保卫王国 动态dp

    题目大意: emmmmm 题解: QAQ #include <cstdio> #include <cstring> #include <iostream> usin ...

  4. P5024 保卫王国(动态dp/整体dp/倍增dp)

    做法(倍增) 最好写的一种 以下0为不选,1为选 \(f_{i,0/1}\)为\(i\)子树的最小值,\(g_{i,0/1}\)为除i子树外的最小值 \(fh_{i,j,0/1,0/1}\)为确定\( ...

  5. LuoguP5024 保卫王国(动态DP,LCT)

    最小权覆盖集 = 全集 - 最大权独立集 强制取点.不取点可以使用把权值改成正无穷或负无穷实现 接下来就是经典的"动态最大权独立集"了 O(nlogn). 这不是我说的,是immo ...

  6. BZOJ 5466: [Noip2018]保卫王国 动态DP

    Code: // luogu-judger-enable-o2 #include<bits/stdc++.h> #define ll long long #define lson (now ...

  7. P5024 保卫王国[倍增+dp]

    窝当然不会ddp啦,要写这题当然是考虑优化裸dp啦,但是这题非常麻烦,于是变成了黑题. 首先,这个是没有上司的舞会模型,求图的带权最大独立集. 不考虑国王的限制条件,有 \[ dp[x][0]+=dp ...

  8. Uoj 441 保卫王国

    Uoj 441 保卫王国 动态 \(dp\) .今天才来写这个题. 设 \(f[u][0/1]\) 表示子树 \(u\) 中不选/选 \(u\) 时的最小权值和,显然有:\(f[u][0]=\sum ...

  9. 动态 DP 学习笔记

    不得不承认,去年提高组 D2T3 对动态 DP 起到了良好的普及效果. 动态 DP 主要用于解决一类问题.这类问题一般原本都是较为简单的树上 DP 问题,但是被套上了丧心病狂的修改点权的操作.举个例子 ...

随机推荐

  1. linux 加密安全之AWK

    密钥 密钥一般是一串字符串或数字,在加密或者解密时传递给加密或者解密算法,以使算法能够正确对明文加密或密文解密. 加密算法 已知的加密算法有对称和非对称加密,也就是说你想进行加解密操作的时候需要具备密 ...

  2. 使用cookie记录用户上次访问网页的时间,并返回到页面

    package com.hopetesting.cookie;import javax.servlet.ServletException;import javax.servlet.annotation ...

  3. 华为云函数中使用云数据库的JavaScript SDK基础入门

    背景介绍 使用云数据库Server端的SDK,此处我以华为提供的官方Demo为例,他们的Demo也已经开源放在了GitHub上,大家需要的可以自行下载. https://github.com/AppG ...

  4. 2020信息安全铁人三项 pwn复盘

    第一赛区 hacknote 程序存在格式化字符串漏洞和uaf,不多说了,很简单. 1 from pwn import * 2 3 p = process('./hacknote') 4 elf = E ...

  5. [BUUCTF]REVERSE——不一样的flag

    不一样的flag 附件 步骤 例行查壳儿,32位程序,无壳儿 32位ida载入,shift+f12检索程序里的字符串,看到了如图标记的字符串,加上下面又上下左右的选项,估计是道迷宫类型的题目 将迷宫字 ...

  6. Spring 容器的启动过程 流程图 自己看源码的梳理 如有错错误 请指正

  7. dart系列之:安全看我,dart中的安全特性null safety

    目录 简介 Non-nullable类型 Nullable List Of Strings 和 List Of Nullable Strings !操作符 late关键字 总结 简介 在Dart 2. ...

  8. Windows Terminal 终端 SSH连接centos7 linux

    1.在Windows Store中安装 Windows Terminal 2.打开Windows Terminal,使用下拉箭头,打开设置. 3.在左侧点击"添加新配置文件",再点 ...

  9. 当在myeclipse里发送邮件有错误时,不妨把环境都改成jdk的

    当在myeclipse里发送邮件有错误时,不妨把环境都改成jdk的, 如果是jre的环境会出错,改成jdk的就行了.

  10. call this的范围

    var f1=function(){this.a="类f1的实例的a属性"}; f1代表一个类: f1.a='对象f1的a属性'; var f2=function(){};//类f ...