第一题:
给出数轴正半轴上N个点的坐标和其权值,给出初始体力值M,人一开始在位置0,体力值会随着走过路程的增加而增加,走多少个单位的路消耗多少体力值。到每个点可以打掉,消耗的体力值就是其权值。求 最多能打掉多少点。 N<=10000,其他<=10^18;
解题过程:
1.一开始就直接想到是贪心,首先人是不可能往左走的,否则不会最优。枚举最后停在哪个点,减去到这个点需要的体力,然后把这个点之前的所有点(包括这个店)的权值排个序,从小到大一个一个打,打到体力没有为止。复杂度分析:枚举N次,每次快拍+扫描一次,总的复杂度O(N*(NlogN+N));显然只能拿到部分分。
2.优化:用一个set,由于枚举是从左往右,只要每次添加一个点即可,不用每次都全部排序。或者用个链表做插入排序。
3.正解:如果走到一个点,此时体力能把它打掉,那就暂时假设把它给打掉,如果不能打掉,那么如果它的权值比之前打过的最大权值小,就替换掉,把体力加回去权值差。
这个过程用堆维护(修改,查询最大值),也可以用STL的优先队列。
4.注意数据要用long long 读入,scanf(“I64d"),scanf(”lld“)蛋疼,求稳的话直接cin,嫌cin太慢可以加个std::ios::sync_with_stdio(false);取消同步,经过试验比scanf还快。这题读入出了差错用了int,直接爆0,本来还迅速想到贪心,写了个堆,很快拍过,心情顿时不错,结果。。只能呵呵呵呵。
参考博文优化输入。
https://www.byvoid.com/zhs/blog/fast-readfile/
第二题:
题目大意:
假设有N个不相同的数字,将他们建成一个堆,求方案数mod (10^9+7);注意堆的性质,必须是完全二叉树。 N<=5000000
解题过程:
1.首先手工模拟一下,发现根据对于N个节点的堆,可以确定它的左右子树的节点数lc,rc,然后方案数就是F[lc]*F[rc]???,其实不然,比如N=6,lc=3,rc=2;左子树的3个节点的方案数是F[3],但是这仅仅是3个节点的堆有多少种形态,而3个节点的权值也可以多种取法,是组合数C(5,3)。 因此F[x]=F[lc]*F[rc]*C(n-1,lc);
2.对于C(i,j),可以用递推的方法求出,但是空间是肯定不够的。。(可以拿60分),所以应该直接由公式推,但是公式里又有除法。所以应该把除法转化为乘法,这里涉及求乘法逆元的知识,参考博文 http://blog.sina.com.cn/s/blog_7c4c33190100s48a.html 。加个记忆化就可以AC。
第三题:
题目大意:
给出M条边,N个点,求一生成树,使得最大边和最小边的权值差最小。 M<=15000,N<=2000;
解题过程:
1.AC算法肯定是想不到啦,然后想写个给力点的骗分算法:题目说要是生成树,其实多加几条边并不影响答案,因为答案只和最小最大边有关。因此先给边从小到大排个序,
枚举左端点,二分右端点,并查集判断能否联通。。 复杂度是O(NMlogM),本来以为能拿个60分的,结果只有30分,数据卡的比较紧。