题目大意

这还不是人尽皆知?

有一棵树, 每个节点放军队的代价是\(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. 【C/C++】拔河比赛/分组/招商银行

    题目:小Z组织训练营同学进行一次拔河比赛,要从n(2≤n≤60,000)个同学中选出两组同学参加(两组人数可能不同).对每组同学而言,如果人数超过1人,那么要求该组内的任意两个同学的体重之差的绝对值不 ...

  2. Intellij IDEA设置自定义类描述信息

    Intellij IDEA设置自定义类描述信息 样图 新建Java类自动生成模板信息:作者,时间,描述和其他信息 步骤 以 IntelliJ IDEA Community Edition 2020.1 ...

  3. 【模型推理】量化实现分享一:详解 min-max 对称量化算法实现

      欢迎关注我的公众号 [极智视界],回复001获取Google编程规范   O_o   >_<   o_O   O_o   ~_~   o_O   大家好,我是极智视界,本文剖析一下 m ...

  4. BUUCTF pwn一分题目

    因为以前做过一些题目,看见1分题目也不太多了,就想着,抓紧点把1分题都刷一下吧.所以开个帖子记录一下,题目简单的话就只贴exp了. [BJDCTF 2nd]secret 这里有一个输入可以进行溢出,n ...

  5. 再识ret2syscall

    当初学rop学到的ret2syscall,对int 0x80中断了解还不是很深,这次又复习了一遍.虽然很简单,但是还是学到了新东西.那么我们就从ret2syscall开始吧. IDA一打开的时候,就看 ...

  6. Table.Combine追加…Combine(Power Query 之 M 语言)

    数据源: 销量表和部门表 目标: 其中一表的数据追加到另一表后面,相同列直接追加,不同列增加新列 操作过程: 选取销量表>[主页]>[追加查询]/[将查询追加为新查询]>选择要追加的 ...

  7. Python的动态语言特性; __slots__属性

    python是动态语言 1. 动态语言的定义 动态编程语言 是 高级程序设计语言 的一个类别,在计算机科学领域已被广泛应用.它是一类 在运行时可以改变其结构的语言 :例如新的函数.对象.甚至代码可以被 ...

  8. 电压-电流转换(一):4-20mA电流环

    在仪表电路中,直流信号通常用作物理测量值的模拟表示,例如温度.压力.流量.重量和运动.最常见的是,直流电流信号优先于直流电压信号使用,因为在从电源(测量设备)到负载(指示器.记录仪或控制器)的整个串联 ...

  9. C++之面试题(4)

    题目描述 来源:牛客网 对于一个有序数组,我们通常采用二分查找的方式来定位某一元素,请编写二分查找的算法,在数组中查找指定元素. 给定一个整数数组A及它的大小n,同时给定要查找的元素val,请返回它在 ...

  10. 【九度OJ】题目1208:10进制 VS 2进制 解题报告

    [九度OJ]题目1208:10进制 VS 2进制 解题报告 标签(空格分隔): 九度OJ 原题地址:http://ac.jobdu.com/problem.php?pid=1208 题目描述: 对于一 ...