2018.01.29【GDOI2018】模拟B组

T1:首先a[1]=b[1]。然后,假设我们知道了a[1~i*2-3],现在要求a[i*2-2]和a[i*2-1],那我们可以这样做。

令s0表示a[1~i*2-3]中有多少个数小于b[i],s1表示a[1~i*2-3]中有多少个数大于b[i]。然后我们来分类讨论一下。

若b[i]在a[1~i*2-3]中出现过,则1、若s0=s1,那么要填入一个大于b[i]和一个小于b[i]的

               2、若s0<s1,那么要填入两个小于b[i]的

               3、若s0>s1,那么要填入两个大于b[i]的

若b[i]没有出现过,则在这两个数中一定要填一个b[i],接下来考虑剩下一个数要填什么

               1、若s0<s1,则填一个小于b[i]的

               2、若s0>s1,则填一个大于b[i]的

                                                注意:s0不可能等于s1。

那么要填那种数我们已经确定了,关键是填那一个数。我们知道b[1]的取值范围是1~n*2-1,b[2]是2~n*2-2,b[3]是3~n*2-3……也就是说i越往后,越极端的数越不可能成为中位数,而且因为这些数比较极端,所以他们更容易满足条件。那么也就得出了结论:每一次填入一个当前还没填的数中最大或最小的,这个可以用标记处理。至于s0和s1,可以用线段树维护。


T2:不会,要用莫比乌斯反演。


T3:首先要发现一个性质:若选出来的叶子节点是合法的,那么序列中的每一个数一定等于一个常数乘一个2的幂。为了方便处理,我们可以枚举这个常数,然后把所有数都除以这个常数,得出一个新的序列。注意,被除数必须是常数的倍数,且商必须是2的幂。得到新序列后,我们考虑用dp。

设f[i][j]表示前i个数选出来的数的和为j的最多个数。那么我们考虑a[i+1]选不选。若要选,则一定要满足以下形式:

|---|

|-8-|---|

|---|-4-|-2-|加上一个2

也就是j为a[i+1]的倍数。若满足这个条件,则可以用f[j]+1更新f[j+a[i]]。

答案就是max(f[2^k]),意思是最终合成一棵树。

优化:首先数组可以滚动。然后为了优化时间,我们可以把所有数先放进一个小根堆里,然后每次取出一个最小值,然后以这个最小值作为常数做一遍dp。每一次把合法的数打上标记,下一次就不以这些数为常数了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值