@atcoder - AGC034E@ Complete Compress
@description@
给定一个 N 个点的树,编号为 1, 2, ..., N。第 i 条边连接 ai 与 bi。
再给定一个长度为 N 的 01 串,第 i 个字符表示 i 号点上是否有一个棋子。
保证至少有一个点有棋子。
你可以执行如下操作若干次:
选择两个距离 >= 2 的棋子,将这两个棋子往靠近对方的方向分别移动 1 条边。
是否可以通过若干次操作将所有棋子集中于一个点。如果可以,求出最小操作次数。
Constraints
2≤N≤2000
|S|=N,只包含 0, 1 且至少有一个 1。
1≤ai,bi≤N(ai≠bi) 且形成一棵树。
Input
输入形式如下:
N
S
a1 b1
a2 b2
:
aN−1 bN−1
Output
如果不可能集中所有棋子,输出 -1;
否则输出最小操作次数。
Sample Input 1
7
0010101
1 2
2 3
1 4
4 5
1 6
6 7
Sample Output 1
3
@solution@
N 这么小,显然可以枚举最后集中的点,然后进行判断。
考虑规定了集中的点后,怎么快速判断是否合法。我们尝试去找操作下的不变量。
显然操作要分成两类:祖先关系与非祖先关系。
祖先关系的操作会将一个点拉低,一个点抬高;另一种操作会同时将两个点抬高。
那么深度和的奇偶性是不会变的。
同时发现两种操作下,深度和要么不变,要么减少 2。而当深度和 = 0 时所有点集中于一点。
那么我们可以忽略祖先操作,只剩下非祖先操作,那么深度和每次严格减少 2。只要可行,最小操作次数 = 深度和 / 2。
当然需要稍微证明一下这个的合法性:祖先关系的操作总可以与下一个操作交换。于是可以将所有祖先操作挪到最后。然后就没用了。
怎么判断是否可行呢?考虑根,如果只对根的不同子树进行操作都可以合法的话自然合法。
求出根的每棵子树的深度和。相当于每次操作选出两棵子树,它们的深度和分别减一。
若干正整数 a1, a2, ..., ak,每次操作同时选两个数减一,问是否可以全部减成 0。这其实是一个很经典的问题。
解决方法是比较 sum - max{ai} 与 max{ai}。如果 sum - max{ai} < max{ai},显然最后会剩下 max{ai} - (sum - max{ai});否则,一定可以消到只剩 0 或 1(取决于 sum{ai} 的奇偶性)。
假如只操作根就是这样。
假如还可以操作其他子树内的点,我们显然应该递归去 max{ai} 的那一棵子树,看最后能够得到最小深度和。
再将最终得到的最小深度和拿去上面的判定条件判断一下,就可以知道当前能够剩下的最小深度和。
@accepted code@
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 2000;
const int INF = (1<<30);
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[MAXN + 5], N;
int f[MAXN + 5], s[MAXN + 5];
void dfs(int x, int fa) {
s[x] = a[x], f[x] = 0;
for(edge *p=adj[x];p;p=p->nxt) {
if( p->to == fa ) continue;
dfs(p->to, x), s[x] += s[p->to];
f[x] += f[p->to] + s[p->to];
}
}
int get(int x, int fa) {
int mx = 0;
for(edge *p=adj[x];p;p=p->nxt) {
if( p->to == fa ) continue;
if( mx == 0 || f[p->to] > f[mx] ) mx = p->to;
}
if( mx == 0 ) return 0;
int k = get(mx, x) + s[mx];
if( f[x] - f[mx] - s[mx] >= k ) return f[x] & 1;
else return k - (f[x] - f[mx] - s[mx]);
}
char S[MAXN + 5];
int main() {
scanf("%d%s", &N, S + 1);
for(int i=1;i<=N;i++) a[i] = S[i] - '0';
for(int i=1;i<N;i++) {
int u, v; scanf("%d%d", &u, &v);
addedge(u, v);
}
int ans = INF;
for(int j=1;j<=N;j++) {
dfs(j, 0);
if( get(j, 0) == 0 )
ans = min(ans, f[j]/2);
}
if( ans == INF ) printf("-1\n");
else printf("%d\n", ans);
}
@details@
惊了,这一场 AGC 的过题人数 E > F > D。
其实 E 还是比较容易往正解想。而且就算不会那个经典的模型,你也可以使用树形 dp 来搞定。
@atcoder - AGC034E@ Complete Compress的更多相关文章
- 「AGC034E」 Complete Compress
「AGC034E」 Complete Compress 显然可以枚举根. 然后把某两棵棋子同时往深度浅的方向提,即对不存在祖先关系的两个棋子进行操作. 如果能到达那么就更新答案. 问题转化为如何判定能 ...
- AT4995-[AGC034E] Complete Compress【树形dp】
正题 题目链接:https://www.luogu.com.cn/problem/AT4995 题目大意 \(n\)个点的一棵树,上面有一些棋子,每次可以选择两个棋子移动到他们之间的路径上相邻的点上, ...
- [atAGC034E]Complete Compress
先考虑枚举最后的点,并以其为根 首先,操作祖先-后代关系是没有意义的,因为以后必然有一次操作会操作祖先使其返回原来的位置,那么必然不如操作后代和那一个点(少一次操作) 考虑某一次操作,总深度和恰好减2 ...
- WC2021 题目清单
Day2 上午 <IOI题型与趣题分析> 来源 题目 完成情况 备注 IOI2002 Day1T1 Frog 已完成 IOI2002 Day1T2 Utopia IOI2002 Day1T ...
- 多校联训 DP 专题
[UR #20]跳蚤电话 将加边变为加点,方案数为 \((n-1)!\) 除以一个数,\(dp\) 每种方案要除的数之和即可. 点击查看代码 #include<bits/stdc++.h> ...
- 【AtCoder】AGC034
AGC034 刷了那么久AtCoder我发现自己还是只会ABCE(手动再见 A - Kenken Race 大意是一个横列,每个点可以跳一步或者跳两步,每个格子是空地或者石头,要求每一步不能走到石头或 ...
- 30个HTML初学者建议
The most difficult aspect of running Nettuts+ is accounting for so many different skill levels. If w ...
- use zlib lib to compress or decompress file
If you want to compress or decompress file when writing C++ code,you can choose zlib library,that's ...
- AtCoder Beginner Contest 103
https://beta.atcoder.jp/contests/abc103 A - Task Scheduling Problem Time Limit: 2 sec / Memory Limit ...
随机推荐
- thinkphp用swiftmailer发邮件demo
QQ邮箱 include_once APPPATH . 'libraries/swiftmailer/swift_required.php'; $transport = Swift_SmtpTrans ...
- 作业-[luogu4396][AHOI2013]-莫队
<题面> 卡常终究比不上算法的优化…… 这是莫队的有点小坑的题, 首先不一定能想到,想到不一定打对,打对不一定打好. 首先你会发现,这个题的时限是很长的- $n$和$m$也是很大的. 于是 ...
- JSP内置对象解析
out对象:(PrintWriter类的实例) 用来向客户端输出信息,除了输出各种信息外还负责对缓冲区进行管理: 主要方法: print / println void 输出数据 newLine() v ...
- 【Django入坑之路】Models操作
1:字段 AutoField(Field) - int自增列,必须填入参数 primary_key=True BigAutoField(AutoField) - bigint自增列,必须填入参数 pr ...
- Docker搭建的MySQL容器出现 "Too many connections 1040" 最大连接数修改完未生效的解决方案
原文:Docker搭建的MySQL容器出现 "Too many connections 1040" 最大连接数修改完未生效的解决方案 版权声明:本文为博主原创文章,未经博主允许不得 ...
- 【流水调度问题】【邻项交换对比】【Johnson法则】洛谷P1080国王游戏/P1248加工生产调度/P2123皇后游戏/P1541爬山
前提说明,因为我比较菜,关于理论性的证明大部分是搬来其他大佬的,相应地方有注明. 我自己写的部分换颜色来便于区分. 邻项交换对比是求一定条件下的最优排序的思想(个人理解).这部分最近做了一些题,就一起 ...
- web前端开发必备技术
1.Vue.js是什么? Vue.js(读音 /vjuː/, 类似于 view) 是一套构建用户界面的 渐进式框架.与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计.Vue 的核心库只关注 ...
- jmeter 通过csv data set config 设置参数化后,执行结果显示为<EOF>
通过csv data set config 设置参数化后,执行结果显示为<EOF>: 反复确认相应的参数的设置均没有问题,其中csv文件编码方式采用uft-8.在csv data set ...
- SQL —— 存储过程
一.什么是存储过程 预先存储好的SQL程序. 保存在SQL Server中(跟视图的存储方式一样) 通过名称和参数执行. 二.存储过程的优点 执行速度更快 允许模块化程序设计 提高系统安全性 减少网络 ...
- could not insert: [com.trs.om.bean.UserLog] The user specified as a definer ('root'@'127.0.0.1') does not exist
2019-07-01 11:24:09,315 [http-8080-24] org.hibernate.util.JDBCExceptionReporter logExceptionsWARN: S ...