@NOIP2018 - D2T3@ 保卫王国
@题目描述@
Z 国有n座城市,n−1 条双向道路,每条双向道路连接两座城市,且任意两座城市 都能通过若干条道路相互到达。
Z 国的国防部长小 Z 要在城市中驻扎军队。驻扎军队需要满足如下几个条件:
(1)一座城市可以驻扎一支军队,也可以不驻扎军队。
(2)由道路直接连接的两座城市中至少要有一座城市驻扎军队。
(3)在城市里驻扎军队会产生花费,在编号为i的城市中驻扎军队的花费是p。
小 Z 很快就规划出了一种驻扎军队的方案,使总花费最小。但是国王又给小 Z 提出了 m 个要求,每个要求规定了其中两座城市是否驻扎军队。小 Z 需要针对每个要求逐一给出回答。
具体而言,如果国王提出的第 j 个要求能够满足上述驻扎条件(不需要考虑第 j 个要求之外的其它要求),则需要给出在此要求前提下驻扎军队的最小开销。如果国王提出的第 j 个要求无法满足,则需要输出−1(1≤j≤m)。现在请你来帮助小 Z。
输入
第 1 行包含两个正整数 n,m 和一个字符串 type,分别表示城市数、要求数和数据类型。type 是一个由大写字母 A,B 或 C 和一个数字 1,2,3 组成的字符串。它可以帮助你获得部分分。你可能不需要用到这个参数。
第 2 行 n 个整数 pi,表示编号 i 城市中驻扎军队的花费。
接下来 n−1 行,每行两个正整数u,v,表示有一条 u 到 v 的双向道路。
接下来 m 行,第 j 行四个整数 a,x,b,y(a≠b),表示第 j 个要求是在城市 a 驻扎 x 支军队, 在城市 b 驻扎 y 支军队。其中,x 、y 的取值只有 0 或 1:若 x 为 0,表示城市 a 不得驻扎军队,若 x 为 1,表示城市 a 必须驻扎军队;若 y 为 0,表示城市 b 不得驻扎军队, 若 y 为 1,表示城市 b 必须驻扎军队。
输入文件中每一行相邻的两个数据之间均用一个空格分隔。
输出
输出共 m 行,每行包含 1 个整数,第 j 行表示在满足国王第 j 个要求时的最小开销, 如果无法满足国王的第 j 个要求,则该行输出 −1。
输入样例#1
5 3 C3
2 4 1 3 9
1 5
5 2
5 3
3 4
1 0 3 0
2 1 3 1
1 0 5 0
输出样例#1
12
7
-1
样例解释
对于第一个要求,在 4 号和 5 号城市驻扎军队时开销最小。
对于第二个要求,在 1 号、2 号、3 号城市驻扎军队时开销最小。
第三个要求是无法满足的,因为在 1 号、5 号城市都不驻扎军队就意味着由道路直接连接的两座城市中都没有驻扎军队。
数据规模与约定
对于 100% 的数据,n,m ≤ 100000,1 ≤ pi ≤ 100000。
@题解@
傻逼倍增题,我竟然不会做,实在是太弱了。
假如不加限制就是一个很简单的 dp。
状态定义为 dp[i][0/1] 表示 i 结点驻扎/不驻扎时,i 为根的子树的最小值。
于是:
\]
基于此,我们最简单最粗暴的想法肯定就是:写动态 dp。假如结点 a 一定驻扎,就修改 dp[a][0] = INF;否则修改 dp[a][1] = INF。
没错考场上我也是这么想的……然后发现自己写不来……
但其实这道题不需要写动态 dp,因为没有修改操作,而只有查询操作。
我们再看看我们按照动态 dp 的想法而实现的暴力:如果修改结点 a, b 的权,就从 a, b 开始往上爬父亲,更新沿途的 dp 值。
可不可以用某种方法加速这个爬父亲的过程呢?
当然可以。我们利用我们求 LCA 用的方法:树上倍增。
具体来说,令\(fa[i][j]\):i 的第 2^j 个父亲;令 \(f[i][j][0/1][0/1]\) :当固定 i 驻扎/不驻扎时,i 的第 2^j 个父亲的 dp[0/1] 的最小值。
如果已知 i 的第 2^(j-1) 个父亲的信息,可以通过枚举 i 的 2^(j-1) 个父亲的驻扎情况,推导出 i 的第 2^j 个父亲的信息。假如令 k = i 的 2^(j-1) 个父亲,举一个转移的例子:
\]
min 的前半部分表示 i 的第 2^(j-1) 个父亲不驻扎,后半部分表示 i 的第 2^(j-1) 个父亲要驻扎。大体来说就是:总答案-不受限制的贡献+受限制的贡献。
边界情况 f[i][0][0/1][0/1] 的求解可以直接借助 dp 数组来搞。
假如询问 a 和 b,我们可以先跳到两者的 LCA 再继续跳到根,求出根的值。因为由上面我们可以 f 是可以合并相互之间的信息的,所以只需要仿照上面的方法就可以实现倍增跳父亲。
具体实现细节可看看代码。
@代码@
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 100000;
const ll INF = (1LL<<60);
struct edge{
int to;
edge *nxt;
}edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt=&edges[0];
void addedge(int u, int v) {
edge *p=(++ecnt);
p->to = v, p->nxt = adj[u], adj[u] = p;
p = (++ecnt);
p->to = u, p->nxt = adj[v], adj[v] = p;
}
int a, x, b, y;
bool g[MAXN + 5];
int p[MAXN + 5], dep[MAXN + 5], fa[MAXN + 5][20];
ll dp[MAXN + 5][2], f[MAXN + 5][20][2][2];
void dfs1(int rt, int pre) {
dep[rt] = dep[pre] + 1;
dp[rt][0] = 0, dp[rt][1] = p[rt];
for(edge *p=adj[rt];p!=NULL;p=p->nxt) {
if( p->to == pre ) continue;
dfs1(p->to, rt);
dp[rt][0] = (dp[rt][0] + dp[p->to][1]);
dp[rt][1] = (dp[rt][1] + min(dp[p->to][0], dp[p->to][1]));
}
}
void dfs2(int rt, int pre) {
fa[rt][0] = pre;
f[rt][0][0][0] = INF;
f[rt][0][1][0] = dp[pre][0];
f[rt][0][0][1] = dp[pre][1] - min(dp[rt][0], dp[rt][1]) + dp[rt][0];
f[rt][0][1][1] = dp[pre][1] - min(dp[rt][0], dp[rt][1]) + dp[rt][1];
for(int i=1;i<20;i++) {
int anc = fa[rt][i-1];
fa[rt][i] = fa[anc][i-1];
f[rt][i][0][0] = min(f[anc][i-1][0][0]-dp[anc][0]+f[rt][i-1][0][0], f[anc][i-1][1][0]-dp[anc][1]+f[rt][i-1][0][1]);
f[rt][i][1][0] = min(f[anc][i-1][0][0]-dp[anc][0]+f[rt][i-1][1][0], f[anc][i-1][1][0]-dp[anc][1]+f[rt][i-1][1][1]);
f[rt][i][0][1] = min(f[anc][i-1][0][1]-dp[anc][0]+f[rt][i-1][0][0], f[anc][i-1][1][1]-dp[anc][1]+f[rt][i-1][0][1]);
f[rt][i][1][1] = min(f[anc][i-1][0][1]-dp[anc][0]+f[rt][i-1][1][0], f[anc][i-1][1][1]-dp[anc][1]+f[rt][i-1][1][1]);
}
for(edge *p=adj[rt];p!=NULL;p=p->nxt) {
if( p->to == pre ) continue;
dfs2(p->to, rt);
}
}
char type[3];
int main() {
int n, m;
scanf("%d%d%s", &n, &m, type);
for(int i=1;i<=n;i++)
scanf("%d", &p[i]);
for(int i=1;i<n;i++) {
int u, v;
scanf("%d%d", &u, &v);
addedge(u, v);
}
dfs1(1, 0); dfs2(1, 0);
for(int i=1;i<=m;i++) {
scanf("%d%d%d%d", &a, &x, &b, &y);
if( x == 0 && y == 0 ) {
if( fa[a][0] == b || fa[b][0] == a ) {
printf("-1\n");
continue;
}
}
if( dep[a] < dep[b] )
swap(a, b), swap(x, y);
ll v1, v2, v3, v4;
v1 = x? dp[a][1] : INF;
v2 = x? INF : dp[a][0];
v3 = y? dp[b][1] : INF;
v4 = y? INF : dp[b][0];
for(int i=19;i>=0;i--) {
if( dep[fa[a][i]] >= dep[b] ) {
ll tmp1, tmp2;
tmp1 = min(f[a][i][1][1] - dp[a][1] + v1, f[a][i][0][1] - dp[a][0] + v2);
tmp2 = min(f[a][i][1][0] - dp[a][1] + v1, f[a][i][0][0] - dp[a][0] + v2);
v1 = tmp1, v2 = tmp2;
a = fa[a][i];
}
}
if( a == b ) {
if( y ) v2 = INF;
else v1 = INF;
}
else {
for(int i=19;i>=0;i--)
if( fa[a][i] != fa[b][i] ) {
ll tmp1, tmp2, tmp3, tmp4;
tmp1 = min(f[a][i][1][1] - dp[a][1] + v1, f[a][i][0][1] - dp[a][0] + v2);
tmp2 = min(f[a][i][1][0] - dp[a][1] + v1, f[a][i][0][0] - dp[a][0] + v2);
tmp3 = min(f[b][i][1][1] - dp[b][1] + v3, f[b][i][0][1] - dp[b][0] + v4);
tmp4 = min(f[b][i][1][0] - dp[b][1] + v3, f[b][i][0][0] - dp[b][0] + v4);
v1 = tmp1, v2 = tmp2, v3 = tmp3, v4 = tmp4;
a = fa[a][i], b = fa[b][i];
}
int pr = fa[a][0];
ll tmp1, tmp2, tmp3 = dp[pr][1]-min(dp[b][0], dp[b][1])-min(dp[a][0], dp[a][1]), tmp4 = dp[pr][0]-dp[a][1]-dp[b][1];
tmp1 = tmp3+min(v1, v2)+min(v3, v4);
tmp2 = tmp4+v1+v3;
v1 = tmp1, v2 = tmp2;
a = pr;
}
for(int i=19;i>=0;i--)
if( dep[fa[a][i]] >= dep[1] ) {
ll tmp1, tmp2;
tmp1 = min(f[a][i][1][1] - dp[a][1] + v1, f[a][i][0][1] - dp[a][0] + v2);
tmp2 = min(f[a][i][1][0] - dp[a][1] + v1, f[a][i][0][0] - dp[a][0] + v2);
v1 = tmp1, v2 = tmp2;
a = fa[a][i];
}
printf("%lld\n", min(v1, v2));
}
}
@NOIP2018 - D2T3@ 保卫王国的更多相关文章
- noip2018 d2t3 保卫王国 解题报告
保卫王国 电脑卡懒得把题面挪过来了. 朴素 \[ dp_{i,0}=\sum dp_{s,1}\\ dp_{i,1}=\sum \min(dp_{s,0},dp_{s,1})+p_i \] 然后直接动 ...
- 「NOIP2018」保卫王国
「NOIP2018保卫王国」 题目描述 有一棵 \(n\) 个点, 点有点权 \(a_i\),\(m\) 组询问, 每次求钦点两个节点必须选或者必须不选后的树上最小点覆盖. \(1 \leq n, m ...
- 【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 Day2T3 保卫王国
首先不考虑强制要求的话是一个经典问题,令 \(f_{i, 0 / 1}\) 为 \(i\) 选或不选时以 \(i\) 为根的子树的最优答案.那么就有转移 \(f_{u, 0} = \sum f_{v, ...
- 竞赛题解 - 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 ...
随机推荐
- 移动端H5适配流程
(一) 由于手机生产商越来越多,不同手机的硬件尺寸又不尽相同,这就给我们的设计适配造成很大困扰.但我们可以围绕从基准分辨率设计,上下进行兼容适配的原则来进行快捷操作.以IOS阵营为例: 图注:移动适配 ...
- NYOJ1367 物流配送
题目描述: 物流配送是物流活动中一种非单一的业务形式,它与物品流动.资金流动紧密结合.备货是配送的准备工作或基础工作,备货工作包括筹集货源.订货或购货.集货.进货及有关的质量检查.结算.交接等.配送的 ...
- POJ 3628 Bookshelf 2【背包型DFS/选or不选】
Bookshelf 2 Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 11105 Accepted: 4928 Desc ...
- Leetcode46. Permutations全排列
给定一个没有重复数字的序列,返回其所有可能的全排列. 示例: 输入: [1,2,3] 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1 ...
- DLedger —基于 raft 协议的 commitlog 存储库
“点击获取上云帮助文档” 尊敬的阿里云用户: 您好!为方便您试用开源 RocketMQ 客户端访问阿里云MQ,我们申请了专门的优惠券,优惠券可以直接抵扣金额.请填写下您公司账号信息,点击上图,了解更多 ...
- WPF Popup实现拖动
问题一.popup总是置顶,遮挡其他窗口 最近发现popup设置打开后,总是会遮挡其他窗口,而我们只想让它仅仅在应用程序的上一层即可,并不像让它在最上面 解决方案是继承Popup重新定义控件Popup ...
- Python 五个知识点搞定作用域
Python 五个知识点搞定作用域 1.块级作用域 想想此时运行下面的程序会有输出吗?执行会成功吗? #块级作用域 if 1 == 1: name = "lzl" print(na ...
- freemarker自定义标签(与java合用)
自定义类继承FreemarkerManager类,重写protected Configuration createConfiguration(ServletContext servletContext ...
- DirectX11笔记(四)--渲染管线
原文:DirectX11笔记(四)--渲染管线 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u010333737/article/details/ ...
- vue-cli3 搭建的前端项目基础模板
基于 vue-cli3 搭建的前端模板,fork 或 clone 本仓库,即可搭建完成一个新项目的基础模板,源码地址,欢迎 star 或 fork 特性 CSS 预编译语言:less Ajax: ax ...