首先是题目及比赛情况:
-
A Simple Tree Problem 10.81% (93/860)
-
The Review Plan I 17.14% (18/105)
-
The Review Plan II 22.97% (34/148)
-
Digging 21.94% (97/442)
-
Choosing number 26.43% (120/454)
-
Flower 22.72% (50/220)
-
GF And RP 13.33% (6/45)
-
Happy Great BG 8.41% (315/3744)
-
Arrangement 5.88% (4/68)
-
Plants vs Fat Sheeps 8.69% (2/23)
正如预计,H 是最简单的一道题,也是坑最多的一道......
================================
ZOJ Monthly, March 2013 解题报告
By 猛犸也钻地
================================
ZOJ 3686 : A Simple Tree Problem
题意:给出一棵树,做 M 次操作,每次要么将一棵子树上每个结点的布尔值取反,要么查询一棵子树上布尔值为 true 的个数
代码:http://ideone.com/VxoVvq
相当裸的题,先 dfs 将树转化成一个序列(求出每个结点的先序遍历值,以及它的所有子节点中的最大先序遍历值,以这两个值作为该结点在序列上的区间左右端点),然后用线段树维护,区间修改/区间查询,非常经典了,一个考察基本功的题目
================================
ZOJ 3687 : The Review Plan I
题意:一门课的书有 N 章内容要复习,每天可以复习一章,但是有 M 个限制条件:在第 D[i] 天中无法复习第 C[i] 章,问不同的复习方案数
代码:http://ideone.com/16bETS(bfs 版)http://ideone.com/CVBLH2(dfs 版)
容斥原理,不考虑约束条件,则一共有 N! 种复习方案,然后减去刚好触发一个约束条件的方案数,加上刚好触发两个约束条件的方案数,减去刚好触发三个的……依此类推
另外注意一点,输入中可能包含重复的约束条件,要先去重再算容斥,否则会得到错误的结果
================================
ZOJ 3688 : The Review Plan II
题意:一门课的书有 N 章内容要复习,每天可以复习一章,但是第 i 章不能在第 i 天或第 i + 1 天复习(最后一章则对应最后一天和第一天),问不同的复习方案数
代码:http://ideone.com/yDOinm
如果只限制第 i 章不能在第 i 天复习,那么明显是错位排列,而现在这个条件该怎么做呢?
嗯,反正我是不知道怎么做,问出题者去吧,我只会找规律......
================================
ZOJ 3689 : Digging
题意:古代某统治者要修建一些棺材,其中第 i 个棺材大小为 s[i],修建需要花费 t[i] 天,如果在剩余 x 天的时候开始修建并且能够及时完成,则能获得 x * s[i] 的报酬,总共有 T 天可用,问最大能获得的报酬为多少
代码:http://ideone.com/eYK6u8
先不考虑总天数 T 的限制,假设他们全都能修建完成。对于某个修建顺序 a[1], a[2], .., a[N],考虑其中任意相邻的两个任务 l = a[i] 和 r = a[i + 1],它们能获得的报酬为:
x * s[l] + (x - t[l]) * s[r] = x * (s[l] + s[r]) - t[l] * s[r]
如果交换它们的顺序,则明显不影响其他任务(因为它们的总耗时不变),而交换后的报酬为:
x * s[r] + (x - t[r]) * s[l] = x * (s[l] + s[r]) - t[r] * s[l]
可以发现,这两个式子变换后,前面的部分都一样,后面的部分一个是 -t[l] * s[r],一个是 -t[r] * s[l]
既然交换相邻的任务不会影响其他任务,但会改变的总报酬,那么我们就可以通过对 {1, 2, 3, .., N} 这个序列进行一定的交换,得到一个报酬最大的修建顺序,换句话说,对这些任务进行一个排序即可得到一个最优的工作顺序:
bool cmp(int l, int r){
return -t[l] * s[r] > -t[r] * s[l];
}
现 在,有了总天数 T 的限制后,必须在这个基础上进行一个 DP,做法就是从排好序的工作中选择一部分工作去执行(上面的 cmp 函数中的表达式能转换 成 t[l] / s[l] < t[r] / s[r],可以发现它有传递性,因此它的任意子序列也是最优的顺序),
于是剩下的 DP 部分是一个类似于背包的写法,照着题目里给的计算方式去算就行了
================================
ZOJ 3690 : Choosing number
题意:n 个人,每人各自从 1 到 m 中选出一个数字,要求是:相邻的两个人若是选择了同一个数字,那么该数字必须大于 k,求方案数
代码:http://ideone.com/LPSNL2
递推,a[i] 表示计算完前 i 个人,最后一个人选出的数字小于等于 k 的方案数,b[i] 则表示相应大于 k 的方案数,很明显,有:
a[i] = a[i - 1] * (k - 1) + b[i - 1] * k
b[i] = a[i - 1] * (m - k) + b[i - 1] * (m - k)
把这个递推式写成矩阵,用矩阵快速幂的方法算一下就可以了
================================
ZOJ 3691 : Flower
题意:给出空间中的一些点,在这些位置上各有 F[i] 朵花,最多可以从这个位置移出 L[i] 朵花(包括从其他地方移过来的),问最小移动半径 R 是多少时,能够把所有位置上的花移动到位置 1
代码:http://ideone.com/ITHM2U
没什么好说的,二分半径后做网络流。为了保证精度,可以预处理出所有距离的平方,然后在这些值里面二分半径
================================
ZOJ 3692 : GF And RP
题意:n 个人排成一排,每个人有两个值 rp[i] 和 gf[i],现要把这 n 个人划分成若干段,要求各段的 max(rp) 加起来不超过 Rlimit,并使得最大的 max(gf) 最小
代码:http://ideone.com/PmNCio
从“最大的 max(gf) 最小”入手,先二分这个最小值 top,题目因此就变成了:在各段 max(gf) 均小于等于 top 的情况下,判断能否存在一种分组方式,使得 max(rp) 之和不超过 Rlimit
接下来的部分是一个 dp,O(n^2) 的 dp 应该不难想到,问题就是如何将这个 O(n^2) 的 dp 进行优化
首先,从第 i 个人开始向前取一些人,对于取了 k 个人时候的 max(rp),如果第 k + 1 个人的 rp 值更小,那么把他取进去不会增加 max(rp),反而可能使答案更小(因为 dp[i] 总是单调非降的,越往前的 dp[i] 自然越小)
因此,有意义的决策并没有多少,它们只会分别位于一系列单调递降的 rp[a[k]] 上,因此 dp 优化的第一步便是用一个单调队列,存储这些有效 rp 值。另外顺便还要维护一个离 i 最远能取到的 j,使得:
rp[a[0]] > rp[a[1]] > ... > rp[a[m]]
a[0] >= j, a[m] = i, sum(gf[j] .. gf[i]) <= top
更新 dp[i] 时,可能的取值除了 dp[j - 1] + rp[a[0]],还可能是上面队列里面的 dp[a[k]] + rp[a[k+1]]
可以注意到,dp[a[k]] + rp[a[k+1]] 这个式子和当前的 i 完全无关,既然如此,我们可以在一个 set
================================
ZOJ 3693 : Happy Great BG
题意:ZJU ACM ICPC 暑假集训结束,为了庆祝这兴奋的一刻,教练 Navi 和 Fancy 决定 BG 参加集训的 N 个小朋友们。他们去了楼外楼吃自助,每个人要花费 W 元,但每 K 个人可以免费一个人,Navi 和 Fancy 会平摊此次 BG 的花销,问他们俩每人要支付多少钱
代码:http://ideone.com/hkG3IK
要点 1:别看错题,一共是有 N + 2 个人去吃饭,不是 N 个人
要点 2:注意 W 可能是小数(题目说了,金钱的最小单位是 0.01 元)
要点 3:即使是除以 2 这种简单的除法,也可能带来浮点误差(因为 W 本身不一定能被浮点数精确表示),所以在输出答案时要加 EPS
// 我真不是故意出这题坑你们的
================================
ZOJ 3694 : Arrangement
题意:有 N 个人,每个人有一个能力值 V[i],要求选出 M 段的人,每段长度在 [L, R] 内,使得选出的那些人的平均能力值尽可能大
代码:http://ideone.com/lVHUQw
二分答案,然后 dp
dp[i] = sum[i] - sum[j] + dp[j] (i - R <= j <= i - L)
上面那个 dp 方程中,-sum[j] + dp[j] 这个部分和 i 无关,因此可以存储在单调队列里,将转移降低到 O(1) 的时间复杂度
此题由于 V[i] 能达到 1e8,又要输出两位小数,因此 double 在精度上可能不够
可以用 long double 做,或是用 long long 表示一个定点小数去做,或者用两个 long long 做 Dinkelbach 迭代都是可以的
一般来说分数规划类型的题目,用迭代会快很多,比如我这个代码在 ZOJ 上只要 310 ms,而二分的做法一般要 2500 ~ 8000 ms
另外需要注意,整数除法是向零取整的,而此题要求向下取整
================================
ZOJ 3695 : Plants vs Fat Sheeps
题意:植物大战肥羊,告诉你地形,肥羊们的移动策略,可建造的防御塔,问如何用最小的花费干掉所有的肥羊
代码:http://ideone.com/Flhy5U
仔细读题后可以发现,肥羊的路线是确定的,可建造防御塔的位置也是有限的,并且这两种防御塔都能同时***他们射程内的所有肥羊
因此,每个位置的每种防御塔带来的收益总是固定的,与防御塔的***决策和肥羊们的出现顺序及密度完全无关
在处理完肥羊行进路线后,便可以转换成一个分组背包问题
================================