NOIP2018 Day2T3 保卫王国
首先不考虑强制要求的话是一个经典问题,令 \(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 保卫王国的更多相关文章
- 「NOIP2018」保卫王国
「NOIP2018保卫王国」 题目描述 有一棵 \(n\) 个点, 点有点权 \(a_i\),\(m\) 组询问, 每次求钦点两个节点必须选或者必须不选后的树上最小点覆盖. \(1 \leq n, m ...
- noip2018 d2t3 保卫王国 解题报告
保卫王国 电脑卡懒得把题面挪过来了. 朴素 \[ dp_{i,0}=\sum dp_{s,1}\\ dp_{i,1}=\sum \min(dp_{s,0},dp_{s,1})+p_i \] 然后直接动 ...
- 【NOIP2018】保卫王国 动态dp
此题场上打了一个正确的$44pts$,接着看错题疯狂$rush$“正确”的$44pts$,后来没$rush$完没将之前的代码$copy$回去,直接变零分了..... 这一题我们显然有一种$O(nm)$ ...
- loj 2955 「NOIP2018」保卫王国 - 树链剖分 - 动态规划
题目传送门 传送门 想抄一个短一点ddp板子.然后照着Jode抄,莫名其妙多了90行和1.3k. Code /** * loj * Problem#2955 * Accepted * Time: 26 ...
- @NOIP2018 - D2T3@ 保卫王国
目录 @题目描述@ @题解@ @代码@ @题目描述@ Z 国有n座城市,n−1 条双向道路,每条双向道路连接两座城市,且任意两座城市 都能通过若干条道路相互到达. Z 国的国防部长小 Z 要在城市中驻 ...
- 竞赛题解 - NOIP2018 保卫王国
\(\mathcal{NOIP2018}\) 保卫王国 - 竞赛题解 按某一个炒鸡dalao名曰 taotao 的话说: \(\ \ \ \ \ \ \ \ \ "一道sb倍增题" ...
- LG5024 保卫王国
题意 题目描述 Z 国有\(n\)座城市,\(n - 1\)条双向道路,每条双向道路连接两座城市,且任意两座城市 都能通过若干条道路相互到达. Z 国的国防部长小 Z 要在城市中驻扎军队.驻扎军队需要 ...
- Uoj 441 保卫王国
Uoj 441 保卫王国 动态 \(dp\) .今天才来写这个题. 设 \(f[u][0/1]\) 表示子树 \(u\) 中不选/选 \(u\) 时的最小权值和,显然有:\(f[u][0]=\sum ...
- [NOIP2018TG]保卫王国
[NOIP2018TG]保卫王国 BZOJ luogu 当动态dp模板题写的,(全集-最大点权独立集)不能放军队的+inf,必须放军队-inf即可 注意矩阵乘法的顺序问题 #define ll lon ...
随机推荐
- mod4最优路径问题(转载)
原文链接:https://blog.csdn.net/ACdreamers/article/details/18501855 mod4最优路径问题 如下图: 从1到4找出一条路径,要求路径的总长度mo ...
- 「算法笔记」旋转 Treap
一.引入 随机数据中,BST 一次操作的期望复杂度为 \(\mathcal{O}(\log n)\). 然而,BST 很容易退化,例如在 BST 中一次插入一个有序序列,将会得到一条链,平均每次操作的 ...
- JSON.parse 和 JSON.stringify 详解
JSON格式,(简写JavaScript Object Notation),是一种用于数据交换的文本格式,书写简单. 基于JavaScript原生语法,能够序列化对象.数组.数值.字符串.布尔值和 n ...
- vue基于Blob.js和 Export2Excel.js做前端导出
1安装三个依赖包 npm install -S file-saver@2.0.2 npm install -S xlsx@0.15.6 npm install -D script-loader@0.7 ...
- C++多线程并发---异步编程
线程同步主要是为了解决对共享数据的竞争访问问题,所以线程同步主要是对共享数据的访问同步化(按照既定的先后次序,一个访问需要阻塞等待前一个访问完成后才能开始).这篇文章谈到的异步编程主要是针对任务或线程 ...
- Redisson分布式锁学习总结:可重入锁 RedissonLock#lock 获取锁源码分析
原文:Redisson分布式锁学习总结:可重入锁 RedissonLock#lock 获取锁源码分析 一.RedissonLock#lock 源码分析 1.根据锁key计算出 slot,一个slot对 ...
- 使用 windows bat 脚本命令一键实现快速配置JDK 环境变量
%1 mshta vbscript:CreateObject("Shell.Application").ShellExecute("cmd.exe"," ...
- SQL Server 数据库添加主键,唯一键,外键约束脚本
-- 声明使用数据库use 数据库;go -- 添加主键(primary key)约束-- 基本语法-- 判断主键约束是否存在,如果存在则删除,不存在则添加if exists(select * fro ...
- MobaXterm远程连接Linux图形用户界面
目标: 在自己的Windows桌面打开运行在Linux上的firefox浏览器, 使用MobaXterm终端工具在命令行直接打开图像化界面. 工具: Windows: MobaXterm Linux: ...
- .NET 微服务——CI/CD(4):避坑和一点经验
如果你看过之前几篇文章,应该已经Jenkins成功搭建了CICD环境,但是进入正式环境会有一些坑,不注意中招的话很难受,这里总结一下,避免重复消耗精力. 后门漏洞 Jenkins有后门,这是个老问题了 ...