首先是题目及比赛情况:

  1. A Simple Tree Problem         10.81% (93/860)

  2. The Review Plan I         17.14% (18/105)

  3. The Review Plan II         22.97% (34/148)

  4. Digging         21.94% (97/442)

  5. Choosing number         26.43% (120/454)

  6. Flower         22.72% (50/220)

  7. GF And RP         13.33% (6/45)

  8. Happy Great BG         8.41% (315/3744)

  9. Arrangement         5.88% (4/68)

  10. 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 存储这些值,并与上面的那个单调队列同步加入删除相应元素,然后在更新 dp[i] 时直接取出最小的就可以了

 

================================

 

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

 

仔细读题后可以发现,肥羊的路线是确定的,可建造防御塔的位置也是有限的,并且这两种防御塔都能同时***他们射程内的所有肥羊

因此,每个位置的每种防御塔带来的收益总是固定的,与防御塔的***决策和肥羊们的出现顺序及密度完全无关

在处理完肥羊行进路线后,便可以转换成一个分组背包问题

 

================================