@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 ...
随机推荐
- 前端(Node.js)(1)-- 初识Node.js
1.认识 Node.js 诞生.发展.应用现状.生态圈等方面 1.1. 2008年 RyanDahl的目标是创建一个易扩展.适用于现代Web应用通信的服务器平台 1.2.国内外的应用情况 Linked ...
- Hackerrank--XOR love (Math系列)
题目链接 Devendra loves the XOR operation very much which is denoted by ∧ sign in most of the programmin ...
- Hackerrank--Ashton and String(后缀数组)
题目链接 Ashton appeared for a job interview and is asked the following question. Arrange all the distin ...
- vmware 安装 黑群晖
先做一个启动盘 然后竟然启动不了 算了 不管了,去网上找个别人做好的吧 添加硬盘的时候,需要选择sata, 比如安装6.2需要这个版本的引导,就直接选中这个,因为我自己做的启动盘不管用,也不知道为嘛 ...
- Leetcode11.Container With Most Water盛最多水的容器
给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) .在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0).找出其中的两条线, ...
- python利用Remove.bg接口自动去背景(转)
转 https://blog.csdn.net/Quentin_he/article/details/97569625 前段时间基友找我让帮忙把他的结婚登记照扣出来换一个背景当作简历照,好在我之前学过 ...
- 数据库Mysql监控及优化
在做 性能测试的时候数据最重要,数据来源于哪里呢,当然是数据库了,数据库中,我们可以知道,数据从磁盘中要比从缓存中读取数据的时间要慢的多的多,还可以知道,同样的一个sql语句,执行的效率也不一样,这是 ...
- php 简单加密解密
<?php namespace App\Service; /* * @link http://kodcloud.com/ * @author warlee | e-mail:kodcloud@q ...
- yii常用操作数据
yii常用操作数据.php <?php defined('YII_DEBUG') or define('YII_DEBUG', true); //当在调试模式下,应用会保留更多日志信息,如果抛出 ...
- CMake学习笔记一
#设置cmake最低版本号 CMAKE_MINIMUM_REQUIRED(VERSION ) #设置工程名称 PROJECT(HelloWorld) #设置工程包含当前目录,非必须 SET(CMAKE ...