首先不考虑强制要求的话是一个经典问题,令 \(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. 编写Java程序,使用PreparedState实现对英雄数据的新增、删除和更新

    返回本章节 返回作业目录 需求说明: 使用PreparedState实现对英雄数据的新增.删除和更新 英雄(t_hero)表结构 列名(含义) 数据类型 约束 id (序号) int 主键,自动增长 ...

  2. docker学习:docker镜像

    镜像是什么 镜像是一种轻量级.可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码.运行时.库.环境变量和配置文件. UnionFS(联合文件系 ...

  3. Java访问Elasticsearch报错Request cannot be executed; I/O reactor status: STOPPED

    简介 使用ES过程中遇到一个Request cannot be executed; I/O reactor status: STOPPED 的异常,大概意思是和server端的连接异常终止了.开始以为 ...

  4. 【JPA】Spring Data JPA 实现分页和条件查询

    文章目录 1.在`Repository`层继承两个接口 2.在Service层进行查询操作 3.Page的方法 1.在Repository层继承两个接口 JpaRepository<Admin, ...

  5. ComfortTypingPro快速录入工具

    1.简介 Comfort Typing Pro 官方中文注册版是一款非常方便的键盘快速录入辅助工具, 也可以叫做键盘宏工具吧. 程序的主要功能是可以帮助你快速的输入大量需要重复输入的内容, 支持文本和 ...

  6. hisql ORM 框架研究(国内第一个支持HANA的ORM框架)

    HiSql 操作说明文档 V1.0 下一代ORM框架 国内第一个支持HANA的ORM框架 hisql源码下载 git clone https://github.com/tansar/HiSql.git ...

  7. 2.OSI各层概述

    应用层 1.用户与网络的界面,所有能和用户交互产生网络流量的程序 典型应用层服务:FTP.SMTP.HTTP 表示层 1.用于处理在两个通信系统中交换信息的标识方式 功能1:数据格式变换(翻译) 功能 ...

  8. vue3.0获取地址栏参数

    方法一 toRaw(route).query.value 方法二 router.currentRoute.value.query

  9. iview 按需引入解决加载慢的问题

    如果出现加载2s以上的情况请先查看服务器是否对大文件进行过压缩优化处理. 按照官方文档把iview引入到vue的项目中,全部引入的时候没问题.当按官方文档显示的按需加载是借助插件babel-plugi ...

  10. iframe页面二次登录问题

    原文链接:iframe页面二次登录问题 生产问题 问题背景 由于历史原因,公司内部系统有一些页面是基于iframe嵌入的其他系统的页面,之前一直运行正常,最近不知什么原因接连出现访问所有iframe页 ...