【AHOI2013复仇】NOIP2012 总结

Posted on 2012-11-14 21:04 Mato_No1 阅读(885) 评论(2)  编辑 收藏 引用 所属分类: 比赛总结
【Day1】
vigenere:不说了;

game:
方法一(本沙茶在现场的做法,60分):
设i的左右手为A[i]和B[i]。二分最大的金币数D,则第i个人要满足其前面所有人(包括国王)的A之积不超过S[i] = (D+1) * B[i] - 1。
考虑站在最后的那个人,显然除了他以外的所有人(包括国王)的A之积不能超过他的S值,如果木有人满足此条件则无解,否则取满足此条件的A值最大的人,放最后(因为若某个合法方案最后不是满足此条件的A值最大的人,则把那个A值最大的人与他交换,仍然是合法方案),然后删掉这个人,转化为子问题,直到所有人都删掉为止,此时必然有解。
此方法时间复杂度为O(N2 * log2MAXW),其中MAXW为D的上限,由于60%的数据D<=109,所以MAXW取109
显然这个方法是不能再加高精度了,否则必T,问题是计算所有人的A之积时会超过long long范围,解决方法:由于A和B的上限是104,D的上限是109,所以有解时必然有所有人的A之积<=109 * 104 * 104 = 1017,因此在过程中若超过这个值,必定无解,直接卡掉。

方法二(正解):
把所有的人按照A*B递增排序,就一定是最优解,剩下的就不解释了(需要高精度乘除单精度)。
证明:
若某方案不是将所有人按照A*B递增排序的,则必然存在i使得A[i]*B[i]>A[i+1]*B[i+1](这里的i是排序后的编号,不是原来的编号),下证交换i和(i+1)之后解必然不会变差。
设i前面所有人(包括国王)的A值之积为S1。
交换前,i的金币数为S1/B[i],(i+1)的金币数为S1*A[i]/B[i+1];
交换后,i的金币数为S1*A[i+1]/B[i],(i+1)的金币数为S1/B[i+1];
注意这里S1*A[i]/B[i+1]显然不小于S1/B[i+1],而S1*A[i]/B[i+1]-S1*A[i+1]/B[i]=S1*(A[i]*B[i]-A[i+1]*B[i+1])/(B[i]*B[i+1])>0,因此交换前(i+1)的金币数不小于交换后i和(i+1)的金币数,而除了i和(i+1)外,其他人在交换前后金币数都不变,因此总的金币数的最大值不会变大,即解不会变差。证毕。

这两种解法都是贪心,但它们是从不同的角度入手的——方法一是“分阶段贪心”,证明需要分别证最优子结构性质和贪心选择性质;方法二是“排序贪心”——在一类有关最优顺序的问题中,一般解法不是贪心,就是二分图或网络流模型(当然小数据也可以状压),如果用贪心,只要找到一种排序方法(对某个值排序),使得若不这样排序则把相邻逆序的元素交换后能够得到不更差的解,做法就是正确的。

drive:(本沙茶想出正解了,可是实在写不完,后来交暴力了囧——真真真真真真真真真真真真真真真真真真真真真真真真真真真真真真悲剧)
首先求出S1[i]和S2[i]分别表示i往右最近的和第二近的位置,这个用平衡树可以解决;
然后,每个位置i拆成两个点i.A和i.B,分别表示从这里出发,A先开和B先开。i.A往S2[i].B(如果有的话)连一条边,边权为两位置距离,i.B往S1[i].A(如果有的话)连一条边,边权为两位置距离。由于不会有环(S1[i]、S2[i]均>i),所以是森林。
然后,设F1[i]和F2[i]分别为从i走到根结点,A开的长度和B开的长度,这个BFS一下就可以了囧。
对于第一问,设W[i]为从i往上走,总长不超过X0时最远能走到哪里,显然只要从下到上求W,类似单调队列乱搞即可;求出W后,枚举每个点i,用记录的F1和F2值可以求出i到W[i]路径上A开的和B开的长度,找比值最小的即可;
对于第二问,可以在求出森林后用树的路径剖分搞,但更好的做法是倍增:设SUM[i][k]表示从i往上走2k条边的总长度,SUM可以在预处理中求出,做第二问时,只需要在SUM里调就行了,一次操作的时间复杂度O(log22N)。
显然这是个数据结构综合题,巨繁琐(估计代码要超过10K),本沙茶当时花了1h+写,后来发现肿么也不可能写完了,就交暴力了囧(早知道一上来就写暴力了,这样或许能想出来game的正解啊啊啊啊啊啊啊啊啊……哭死……)

总之Day1跪得不成人形了……

【Day2】
mod: 不说了;

classroom:线段树是可以AC的,只不过要把两个操作(找最小值和区间减法)放一起;
当然,本题的正解是,二分M0,然后看前M0个操作是否能全部满足:将每个操作(l, r, D)拆成(1, r, D)和(1, l-1, -D)(当l>1时),这样所有操作的左端点都是1了,设S[i]为右端点为i的所有操作的D之和,这个显然可以在线性时间内得到,则点i的变化量就是S[i..N]之和,这个在求出S[i]后从后到前扫描一下即可得出,也是线性,如果所有的变化量加上原来的值都不小于0,则前M0个操作都能满足,否则就不是都能满足。
这样,时间复杂度仍是O(NlogM)的。更好的方法是,借鉴倍增的思想,如果前M0个可以满足,就把前M0个都满足(把所有点都加上变化量),接下来就不用考虑前M0个操作了,只在后面继续二分。这样,算法的时间复杂度就是线性的了。

blockade:
【首先每个军队往上走当然是最好的,但不能在根上停住。
因此,二分最长时间D(注意D<=5*10^13),然后先看不能走到根的军队,一直往上走直到时间到了。
然后看能走到根的,先让他们全到根上,然后,对于那些“还有叶结点木有被控制的”根的子树(防止断句错误),用那些走到根上的来控制,用贪心解决……
本沙茶就这么写的,写了180+行,也查完了,可是在结束前5min时突然发现漏了一种情况——
某些能到根的军队可以不走到根,直接停在根的子结点处,控制这棵子树。
可是已经来不及了,最后就木有考虑这种情况……而且这种情况还比较普遍……】
这种情况的解决办法:
如果某个军队能走到根,但让它再走一步,走回到它所在的那个根的子结点,就来不及了的话,就让它停在根的子结点处,控制它自己的子树。这是因为,如果它去帮别的子树,必然就有第三个军队要来帮它。设它所在的那个根的子结点为B,它去帮的子结点为A,帮它的第三个军队所在的根的子结点为C,由于它自己走不回来,所以第一次走到根的子结点处的剩余时间T<2*W(root, B),而它能去帮别的子结点,所以T>=W(root, A)+W(root, B_,可得出W(root, A)<W(root, B)。第三个军队能来帮它,所以第三个军队剩余的时间>=W(root, B)+W(root, C_,自然>W(root, A)+W(root, C),所以这第三个军队也能去帮A,因此,让原来的军队停在B,第三个军队去帮A,也是合法方案。
注:本题灰常麻烦,需要用到树DFS、BFS、倍增(这个和drive肿么一样啊囧……),本沙茶写的时候还用上了线段树囧……

总之Day2也跪了,但不像Day1那么惨囧……
总之Day2比Day1跪得更惨囧……

Feedback

# re: 【AHOI2013复仇】NOIP2012 总结[未登录]  回复  更多评论   

2012-11-15 12:19 by xiaodao
。。。目测省队无压力吧。。QAQ...

# re: 【AHOI2013复仇】NOIP2012 总结  回复  更多评论   

2012-11-20 20:57 by Mato_No1
@xiaodao
省队……毕竟还有明年的成绩
但是,本沙茶现在已经被贺神虐了100+分了……翻盘压力巨大啊囧……

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理