首先不考虑强制要求的话是一个经典问题,令 \(f_{i, 0 / 1}\) 为 \(i\) 选或不选时以 \(i\) 为根的子树的最优答案。那么就有转移 \(f_{u, 0} = \sum f_{v, 1}, f_{u, 1} = \sum \min(f_{v, 0}, f_{v, 1})\).每次查询重新暴力 \(dp\) 一遍整棵树就可以获得 \(44pts\).

先考虑一个部分分,\(B1\) 的情况,可以发现我们每次强制一个点选或不选能影响到的是其到根一条路径上的 \(dp\) 值,那么如果我们每次询问就只需要修改一个点到根路径上的 \(dp\) 值即可,但是如果暴力修改复杂度还是不正确的,如果是菊花图就没了,但同时我们需要知道这样一个事情,通过上面的转移方程是可以发现这个 \(dp\) 值是满足可加性的,换句话说如果我们去除掉某个子树对答案的影响,那么其 \(dp\) 求出来的将会是剩余部分的最优解,也就是说我们的 \(dp\) 是可以分开考虑然后将 \(dp\) 值简单相加。于是我们可以记录 \(g_{i, 0 / 1}\) 表示 \(i\) 选或不选时去除掉 \(i\) 所在子树其父亲的最优解,那么这样我们每次往上重新 \(dp\) 的时候复杂度就是 \(O(dep)\) 了。

可以发现这个过程是可以使用倍增优化的,每次我们往父亲跳的过程可以直接倍增地跳,那么这样就可以快速重新 \(dp\) 出答案了。具体的我们可以令 \(dp_{i, j, 0 / 1}\) 表示在以 \(i\) 的 \(2 ^ j\) 祖先为根的子树中去除掉以 \(i\) 为根的子树部分 \(i\) 选或不选的答案,但是这样我们可以发现一个问题,每次往上跳之后我们并不知道当前跳到节点的状态,因此我们需要再添加一维状态令 \(dp_{i, j, 0 / 1, 0 / 1}\) 表示以 \(i\) 的 \(2 ^ j\) 祖先为根的子树中去除掉以 \(i\) 为根的子树部分 \(i\) 选或不选,\(i\) 的 \(2 ^ j\) 祖先选或不选的答案,那么这样我们就可以一路 \(dp\) 上去了,实现代码的时候最后两个点的 \(lca\) 往根节点跳的过程可以提前预处理出来这样就可以省掉大量的代码(令 \(g_{i, 0 / 1}\) 表示整棵树去除掉以 \(i\) 为根的子树 \(i\) 选或不选的答案),细节比较多,一定要想清楚细节再开始写代码。

#include<bits/stdc++.h>
using namespace std;
#define N 100000 + 5
#define M 20
#define inf 10000000000000000
#define rep(i, l, r) for(register int i = l; i <= r; ++i)
#define dep(i, l, r) for(register int i = r; i >= l; --i)
#define Next(i, u) for(register int i = h[u]; i; i = e[i].next)
typedef long long ll;
struct edge{
int v, next;
}e[N << 1];
char type[M];
ll f[N][2], g[N][2], dp[N][M][2][2];
int n, m, u, v, a, x, b, y, tot, h[N], p[N], dep[N], fa[N][M];
inline int read(){
char c; int x = 0, f = 1;
c = getchar();
while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
inline void add(int u, int v){
e[++tot].v = v, e[tot].next = h[u], h[u] = tot;
}
inline void dfs1(int u, int Fa){
fa[u][0] = Fa, f[u][1] += p[u], dep[u] = dep[Fa] + 1;
Next(i, u){
int v = e[i].v; if(v == Fa) continue;
dfs1(v, u);
f[u][0] += f[v][1], f[u][1] += min(f[v][0], f[v][1]);
}
}
inline void dfs2(int u, int fa){
if(u != 1){
g[u][0] = g[fa][1] + f[fa][1] - min(f[u][0], f[u][1]);
g[u][1] = min(g[fa][1] + f[fa][1] - min(f[u][0], f[u][1]), g[fa][0] + f[fa][0] - f[u][1]);
dp[u][0][0][0] = inf, dp[u][0][0][1] = f[fa][1] - min(f[u][0], f[u][1]);
dp[u][0][1][0] = f[fa][0] - f[u][1], dp[u][0][1][1] = f[fa][1] - min(f[u][0], f[u][1]);
}
Next(i, u) if(e[i].v != fa) dfs2(e[i].v, u);
}
inline ll solve(int x, int a, int y, int b){
if(dep[x] < dep[y]) swap(x, y), swap(a, b);
Next(i, y) if(e[i].v == x && !a && !b) return -1;
int fx = x, fy = y;
ll l[2] = {0, 0}, r[2] = {0, 0}, ans = 0, l0, l1, r0, r1;
l[!a] = inf, r[!b] = inf;
dep(i, 0, 17) if(dep[fa[x][i]] >= dep[y]){
l0 = l[0], l1 = l[1];
l[0] = min(l0 + dp[x][i][0][0], l1 + dp[x][i][1][0]);
l[1] = min(l0 + dp[x][i][0][1], l1 + dp[x][i][1][1]);
x = fa[x][i];
}
if(x != y){
dep(i, 0, 17) if(fa[x][i] != fa[y][i]){
l0 = l[0], l1 = l[1], r0 = r[0], r1 = r[1];
l[0] = min(l0 + dp[x][i][0][0], l1 + dp[x][i][1][0]);
l[1] = min(l0 + dp[x][i][0][1], l1 + dp[x][i][1][1]);
r[0] = min(r0 + dp[y][i][0][0], r1 + dp[y][i][1][0]);
r[1] = min(r0 + dp[y][i][0][1], r1 + dp[y][i][1][1]);
x = fa[x][i], y = fa[y][i];
}
l0 = l[0], l1 = l[1], r0 = r[0], r1 = r[1];
ll l0r0, l0r1, l1r0, l1r1, lca = fa[x][0];
l0r0 = (dp[x][0][0][0] + dp[y][0][0][0] - f[y][1] - f[x][1]) / 2 + g[lca][0];
l0r0 = min(l0r0, (dp[x][0][0][1] + dp[y][0][0][1] - min(f[x][0], f[x][1]) - min(f[y][0], f[y][1])) / 2 + g[lca][1]);
l0r0 += l0 + r0;
l0r1 = (dp[x][0][0][0] + dp[y][0][1][0] - f[y][1] - f[x][1]) / 2 + g[lca][0];
l0r1 = min(l0r1, (dp[x][0][0][1] + dp[y][0][1][1] - min(f[x][0], f[x][1]) - min(f[y][0], f[y][1])) / 2 + g[lca][1]);
l0r1 += l0 + r1;
l1r0 = (dp[x][0][1][0] + dp[y][0][0][0] - f[y][1] - f[x][1]) / 2 + g[lca][0];
l1r0 = min(l1r0, (dp[x][0][1][1] + dp[y][0][0][1] - min(f[x][0], f[x][1]) - min(f[y][0], f[y][1])) / 2 + g[lca][1]);
l1r0 += l1 + r0;
l1r1 = (dp[x][0][1][0] + dp[y][0][1][0] - f[y][1] - f[x][1]) / 2 + g[lca][0];
l1r1 = min(l1r1, (dp[x][0][1][1] + dp[y][0][1][1] - min(f[x][0], f[x][1]) - min(f[y][0], f[y][1])) / 2 + g[lca][1]);
l1r1 += l1 + r1;
x = fa[x][0], y = fa[y][0];
ans += min(min(min(l0r0, l0r1), l1r0), l1r1) + f[fx][a] + f[fy][b];
}
else{
ans += f[fx][a];
l[!b] = inf;
ans += min(l[0] + g[y][0], l[1] + g[y][1]);
}
return ans;
}
signed main(){
n = read(), m = read(), scanf("%s", type + 1);
rep(i, 1, n) p[i] = read();
rep(i, 1, n - 1) u = read(), v = read(), add(u, v), add(v, u);
dfs1(1, 0), dfs2(1, 0);
rep(j, 1, 17) rep(i, 1, n){
int Fa = fa[i][j - 1];
fa[i][j] = fa[Fa][j - 1];
dp[i][j][0][0] = min(dp[i][j - 1][0][0] + dp[Fa][j - 1][0][0], dp[i][j - 1][0][1] + dp[Fa][j - 1][1][0]);
dp[i][j][0][1] = min(dp[i][j - 1][0][0] + dp[Fa][j - 1][0][1], dp[i][j - 1][0][1] + dp[Fa][j - 1][1][1]);
dp[i][j][1][0] = min(dp[i][j - 1][1][0] + dp[Fa][j - 1][0][0], dp[i][j - 1][1][1] + dp[Fa][j - 1][1][0]);
dp[i][j][1][1] = min(dp[i][j - 1][1][0] + dp[Fa][j - 1][0][1], dp[i][j - 1][1][1] + dp[Fa][j - 1][1][1]);
}
while(m--) a = read(), x = read(), b = read(), y = read(), printf("%lld\n", solve(a, x, b, y));
return 0;
}

NOIP2018 Day2T3 保卫王国的更多相关文章

  1. 「NOIP2018」保卫王国

    「NOIP2018保卫王国」 题目描述 有一棵 \(n\) 个点, 点有点权 \(a_i\),\(m\) 组询问, 每次求钦点两个节点必须选或者必须不选后的树上最小点覆盖. \(1 \leq n, m ...

  2. noip2018 d2t3 保卫王国 解题报告

    保卫王国 电脑卡懒得把题面挪过来了. 朴素 \[ dp_{i,0}=\sum dp_{s,1}\\ dp_{i,1}=\sum \min(dp_{s,0},dp_{s,1})+p_i \] 然后直接动 ...

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

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

  4. loj 2955 「NOIP2018」保卫王国 - 树链剖分 - 动态规划

    题目传送门 传送门 想抄一个短一点ddp板子.然后照着Jode抄,莫名其妙多了90行和1.3k. Code /** * loj * Problem#2955 * Accepted * Time: 26 ...

  5. @NOIP2018 - D2T3@ 保卫王国

    目录 @题目描述@ @题解@ @代码@ @题目描述@ Z 国有n座城市,n−1 条双向道路,每条双向道路连接两座城市,且任意两座城市 都能通过若干条道路相互到达. Z 国的国防部长小 Z 要在城市中驻 ...

  6. 竞赛题解 - NOIP2018 保卫王国

    \(\mathcal{NOIP2018}\) 保卫王国 - 竞赛题解 按某一个炒鸡dalao名曰 taotao 的话说: \(\ \ \ \ \ \ \ \ \ "一道sb倍增题" ...

  7. LG5024 保卫王国

    题意 题目描述 Z 国有\(n\)座城市,\(n - 1\)条双向道路,每条双向道路连接两座城市,且任意两座城市 都能通过若干条道路相互到达. Z 国的国防部长小 Z 要在城市中驻扎军队.驻扎军队需要 ...

  8. Uoj 441 保卫王国

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

  9. [NOIP2018TG]保卫王国

    [NOIP2018TG]保卫王国 BZOJ luogu 当动态dp模板题写的,(全集-最大点权独立集)不能放军队的+inf,必须放军队-inf即可 注意矩阵乘法的顺序问题 #define ll lon ...

随机推荐

  1. 警惕!PHP、Node、Ruby 和 Python 应用,漏洞还没结束!

    12 月 10 日凌晨,Apache 开源项目 Log4j2 的远程代码执行漏洞细节被公开,作为当前全球使用最广泛的 java 日志框架之一.该漏洞影响着很多全球使用量前列的开源组件,如 Apache ...

  2. 汇编MMX实现图片淡入淡出核心代码

    计算机组成课程个人作业 参考: https://blog.csdn.net/yangjianqiao0/article/details/69388595 https://blog.csdn.net/d ...

  3. Kronecker Products and Stack Operator

    目录 定义 Stack Operator Kronecker Product 性质 Stack Operator Kronecker Product 半线性 Whitcomb L. Notes on ...

  4. 你真的会用react hooks?看看eslint警告吧!(如何发请求、提升代码性能等问题)

    前言 看过几个react hooks 的项目,控制台上几百条警告,大多是语法不规范,react hooks 使用有风险,也有项目直接没开eslint.当然,这些项目肯定跑起来了,因为react自身或者 ...

  5. MySQL数据库基础(1)数据库基础

    目录 一.数据库简介 二.mysql数据库 三.客户端连接mysql服务 四.Navicat for mysql 一.数据库简介 1.概念 (1)数据:如文字.图形.图像.声音以及学生的档案记录等,这 ...

  6. Swoole 协程的并发调用及使用示例

    示例一: 利用通道pop会自动挂起当前协程,等待生产者推送数据的特性,实现并发调用,并在协程完成后组合结果集. $serv = new Swoole\Http\Server("127.0.0 ...

  7. 初识python 之 取101到200之前的所有素数

    素数:只能被1或本身整除 思路分析:这个数只有2个数据能整除 代码如下: n = 0 li = [] for i in range(101,200): m = 0 for j in range(1,2 ...

  8. centos7安装wordpress详细教程

    安装之前:建议安装各种软件 [root@localhost ~]# yum -y install wget zip unzip net-tools 一.安装apache 测试环境建议关闭防火墙和SEL ...

  9. java list 类型删除其中的某些元素的正确方法

    List<Object> list= new ArrayList<>();//记录需要删除的元素List<Object> li = new ArrayList< ...

  10. github 创建网络仓库 ,使用git工具将本地文件上传/删除 --- 心得

    1.前言 使用  git做项目控制版本工具,当然,使用SVN也可以,但是,git让人感觉更先进一些,与GitHub结合,用起来很方便,服务端由官网控制. 而SVN分客户端和服务端,都是个人控制,因此, ...