贪心(qwq)习题题解
SCOI 题解
[ SCOI2016 美味 ]
假设已经确定了前i位,那么答案ans一定属于一个区间。
从高位往低位贪心,每次区间查找是否存在使此位答案为1的值。
比如6位数确定了前三位\((101...)_2\),下一位应该是1
那么\(a_i+x_i\)的查找区间为:\([(101100)_2,(101111)_2]\) ,同理如果应该是0则为\([(101000)_2,(101011)_2]\)。
注意到有区间\([l,r]\)范围的限制所以用主席树维护即可。
[ SCOI2015 国旗计划 ]
由于有:没有区间被其他区间包含 这个条件,所以满足贪心性质。
先把环拉成序列,然后对于一个右端点\(r_i\),挑选右端点最靠右的且满足\(l_j(l_j\leq r_i)\)的区间即可。
[ SCOI2010 游戏 ]
把每个\((a,b)\)组看成一条边,最终我们构成了很多联通块,我们要将边与点配对。
一共两种情况:
( 1 ) 边数 = 点数-1 , 这种情况下如一棵树,定有一个点不能被匹配。
( 2 ) 边数 >= 点数 ,这种情况下一定有环,每个点都能匹配。
所以用并查集维护联通块,
如果两个点已经相连则匹配根(还未匹配),否则匹配两者中标号较小的并指向较大的点。
[ SCOI2008 斜堆 ]
题解太长,见这里:戳戳戳!( qwq )
HEOI 题解
[ HEOI2015 定价 ]
细节较多的贪心,显然贪心顺序:位数(去掉后导0)> 尾数为5 > 最小化
给定\(L\)与\(R\),先计算出两者的位数\(lg_1\)与\(lg_2\)。
如果\(lg_1!=lg_2\),那么尽量用\(5*10^t\),如果不行则使最大位增大\(1\),然后补\(0\)。
否则从高位往低位贪心,每次依照贪心原则处理即可。
[ HEOI2015 兔子与樱花 ]
设\(f_u\)表示第u个点最多可删节点数,\(g_u\)表示在\(f_u\)最优的前提下的最小负重。
每到一个节点,将儿子按照\(g_u\)从小到大排序,能删则删即可。
正确性? 每个儿子节点\(v\)给父节点\(u\)的贡献为\(g_v-1\)。
那么我们不删这个儿子\(v\)的唯一好处就是使得父节点\(u\)可删了。
所以我们唯一可能的收益就是多删去一个节点\(u\)。
而删除儿子则至少会删除一个,所以稳赚不亏,贪心是正确的。
SDOI 题解
[ Sdoi2011 消防 ]
首先可以证明所选路径一定是直径的一段。证明如下:
我们只需证明路径的两个端点一定在直径上即可。
假设一个端点不在直径上,那么它一定是从某个位置岔开了。
岔开后,岔开出去的位置所在子树内的最大距离减小了。
但是我们可以发现:
因为岔开而未选的直径端点所在子树内的最大距离一定是大于这个距离的。
这个非常显然,直径的定义不就是这样的吗?
所以显然不岔开,继续选择直径上的端点要更优。
所以\(OK\),有了这个结论后剩下的就是一些比较基础的处理了。
预先\(DP\)出直径以及直径上的点,然后处理出某个点为路径左或右端点时的最大距离。
最后搞一个双端队列,双指针扫一遍取最优解即可,时间复杂度\(O(n)\)。
CQOI 题解
[ Cqoi2017 小Q的棋盘]
显然最长链一路到底不回头,其它路径的都折返是最优的。
搜一下最长链,然后分类讨论即可,数据真的很水,\(O(n)\)算法\(n\leq 100\)?
[ cqoi2012 模拟工厂 ]
订单数\(n\leq 15\)明摆着就是要你爆搜订单了,考虑如何验证订单是否可以满足。
显然先提升生产力再制造商品更优。
我们假设当前生产力为\(s\),此订单到下一个订单之间的时间为\(t\),
设下一个订单所需要的总产品数为\(a_q\)(\(\sum_{i=1}^q g_i\)),此时已经有\(nw\)个。
假设这一段的时间提升\(x\)次生产力,那么有以下式子:
\[(s+x)(t-x) \ge a_q - nw\]
把这个式子展开后,发现这是一个开口向下的抛物线,解集是一个区间。
由于对于后面的所有订单,此式子都成立,
所以如果\(a_e < nw\) , 则需要解这个方程,这样我们得到了很多个区间\([l_j,r_j]\)。
一个显然的贪心是在满足这些区间的条件下,这一轮提升的生产力尽量多。
所以\(x = min\{r_j\}\)。\(nw+=(s+x)(t-x)\),\(s=s+x\)。
你可以这样想,最多花多少时间提高生产力可以满足:如果用接下来的时间都生产的话不至于失败?
我们并不是只在一个区间里提升产率,所以这是最优的。
HAOI 题解
[ HAOI2007 覆盖问题 ]
显然边角上的点必须去搞一个矩形覆盖它......
所以二分一个\(L\),然后每次都贪心的去覆盖四个角中的一个即可。
[ HAOI2006 旅行comf ]
类似\(Mst\),枚举边权最大的那条边。
然后按边权从大到小加边,直到两点联通,统计答案即可。
[ HAOI2008 糖果传递 ]
貌似是均分纸牌的升级版?设平均数为\(p\),原有纸牌...糖果!有\(a_i\)个。
设\(e_i\)为\(i\)给\(i+1\)的糖果数,那么有\(e_{i-1}+a_i-e_i = p\)
移项以后:\(e_i = e_{i-1}-(p-a_i)\)。
所以把每个式子展开后有:\(e_i = e_1 - \sum_{k=2}^i(p-a_k)\)
令\(c_i = \sum_{k=2}^i (p-a_k)\),就可以得到(n-1)个\(c_i\ ,\ i\in [2,n]\)。
此时目标变为最小化\(|e_1|+|e_1-c_2|+.....+|e_1-c_n|\)。
这不是小学奥数吗?贪心取数轴的最中心位置即可。
[ HAOI2007 反素数ant ]
显然就是要求不超过\(n\)的数中约数最多的数中最小的一个。
首先\(E = p_1^{k_1}p_2^{k_2}...p_e^{k_e}\)一共有\((k_1+1)(k_2+1)...(k_e+1)\)个约数。
根据反素数的定义:
( 1 ) \(p_1\)、\(p_2\)、...\(p_e\)一定是连续的\(e\)个素数(因为要最小化)。
( 2 ) \(k_1\ge k_2 ...\ge k_e\),原因同上。
而\(2×3×5×7×11×13×17×19×23×29>2,000,000,000\)
所以爆搜一波这10个素数,用( 2 )中的技巧剪枝搞一搞即可。
[ HAOI2007 上升序列 ]
这题估计当年也是全场(ˉ▽ ̄~) 切~~吧...,毕竟太水了。
先预处理出\(f_i\)表示从\(i\)开始向后还可以选多少个。
然后对于每个询问,从前往后贪心,若\(f_i+now\ge need\)则选即可。
JSOI题解
[ JSOI2007 建筑抢修 ]
经典的后悔操作了,先把所有建筑按照\(T_2\)排序。
然后每修理一个建筑就把它的\(T_1\)加入堆中,每次修不了的时候取堆顶比较。
如果堆顶大于此建筑的\(T_1\)则后悔,否则放弃这个建筑。
因为堆中都放的是先修的,所以放弃堆中的建筑就一定可以修当前的建筑。
[ JSOI2010 Group 部落划分 ]
同学你做过关押罪犯吗?这不就是一样的吗?
把点对按照距离从小到大排好序尝试联通,假设当前两点不联通:
如果联通块数目还大于\(K\)则合并两个联通块,否则输出此时的距离即可。
[ JSOI2007 麻将 ]
数据贼小,直接暴力枚举等待牌与对子即可。
然后问题转化为如何\(O(n)\)判定是否能够组成刻子与顺子。
贪心:如果某种牌数量\(x\ge 3\),那么优先组成刻子,剩下\(x\%3\)的牌用作顺子。
为什么是对的?
因为如果这张牌超过3张可以组成顺子,说明后面的牌也超过\(3\)张,可以组成刻子。
HNOI 题解
[ HNOI2003 消防局的设立 ]
显然树的叶子节点一定要用一个点来管辖。
所以每次找到最深的叶子节点,然后向上跳到最远可管辖点,放一个消防局即可。
[ HNOI2010 取石子游戏 ]
题目的意思是:给两个栈和一些双端队列,问先后手最优方案下的取值。
先看栈,栈的取值其实是固定了的。
对于栈\([a_e a_{e-1} ....a_2 a_1>\),如果有\(a_e > a_{e-1}\),
那么当前的先手(当前取的人)去取\(a_{e-1}\)肯定是不优的,因为对手立刻可以取掉\(a_{e}\)。
我们把这种情况放到最后,按照奇偶顺序去取。
然后是双端队列,双端队列的处理则比较巧妙:
对于双端队列\(<a_1 a_2....a_{e-1}a_e>\),如果存在\(a_{i-1} < a_i > a_{i+1}\)这样的上凸关系,
那么此时的先手与后手的收益差其实是确定了的,
所以我们把收益替换为:\(a_{new} = a_{i-1} + a_{i+1} - a_i\)。
由于这样合并后,先后手收益关系并没有变,
所以这样不断变化,序列每个元素其实就变为了先后手收益差。
注意理解这里:这里的先后手收益差中的 先手是指 第一个取这个元素所代表的石子堆集合的人。
然后把元素从大到小排序,依次取,得到最终两者的数量差,解方程即可的到两人的最终石子数。
APOI 题解
[ APIO2008 免费道路 ]
难道是\(Apio\)送分题?(还是年代过于久远?)
先用权值为\(1\)的道路做一遍\(Kruskal\),然后用权值为\(0\)的再做一遍即可。
[ Apio2009 会议中心 ]
题解太长了,请戳这里( qwq )。
[ apio2012 守卫 guard ]
与上一题非常相似,所以这里就简要说了。
显然包含关系只用保留被包含的区间即可,把无用区间删掉后重新标一下号。
此时区间的左右端点都是单调递增的了。
那么一个点是必须的,当且仅当不选它时,所需忍者个数大于\(K\)。
你可以这样理解:如果必须忍者数少于\(K\),多余的忍者随便放。
所以考虑如何放最少的忍者来满足所有区间的要求。
显然是放在每个区间的最右端点,因为这样可以影响最多的后面区间。
预处理\(f_i\)表示满足前\(i\)个区间最少放几个忍者,\(g_i\)表示\(i\)之后的区间最少要几个。
处理方法同上面所说的贪心,只是\(g_i\)贪心左端点罢了。
然后从前往后枚举每一个区间的右端点,显然次优位置为\(R-1\)位置(右端点-1)。
假设在这个位置放忍者,那么找到\(p_l\),\(p_r\)(其不能满足的首个左边/右边区间)。
显然根据上面的必须条件,这个右端点必须的条件为:\(f_{p_l} + g_{p_r} + 1 > K\)。
注意特判,如果删去不合法区间后,位置只剩下\(K\)个,那这\(K\)个位置一定都是忍者。
然后就做完了,并且这题相比于上题来说,代码写起来舒服得多。
[ Apio2015 巴厘岛的雕塑 ]
看到位运算的极值问题这题就稳了一半......(qwq)
显然从大到小枚举每一位,然后尽量让答案\(ans\)这一位为0。
然后发现题目要求每一组必须是连续的一段。 这不就很好做了吗?
对于前4个\(SubTask\),\(n\leq 100\ ,\ A \ge 1\)。
假设枚举到了第\(E\)位,设\(f[i][j]\)表示把前\(i\)个数划分为\(j\)组,
且所形成答案对于之前枚举的每一位都与\(ans\)相同,且当前枚举位为0的可行性。
转移时枚举\([k,i]\)为1组,考虑转移条件:
( 1 )之前的位都与\(ans\)一致:\(((\sum_{t=j}^i Y_i >>E)<<E) | ans == ans\)
( 2 )当前枚举位为\(0\):\((\sum_{t=j}^i Y_{i}) \& (1<<(E-1)) == 0\)
如果能转移,则\(f[i][j]= f[i][j]\ |\ f[k-1][j-1]\),前4个点就做完了。
然后\(SubTask5\)简直就是搞笑的。\(A=1,n\leq 2000\)即无下界限制。
所以把第二维去掉:\(f_i\)表示前\(i\)个数分组且满足条件的最少组数,最后判一下\(f_n \leq B\)即可。
NOI 题解
[ NOI2008 假面舞会 ]
题解较长,具体题解请戳这里。
[ NOI2014 随机数生成器 ]
前面矩阵的生成直接大模拟即可。然后考虑贪心。
由于是排列,显然小数字优先要出现,所以从1开始枚举,
假设能选则选,然后把选了此数后路径上不可能出现的位置都删去即可。