二模 (7) day1
第一题:
题目大意:
给出数轴上N棵树的坐标和高度,如果两棵树之间的距离小于其中一颗树的高度,那么就有树会被挡住。因此要把一些树砍矮一点。求砍树的总高度最小值。
N<=100000;
解题过程:
1.水题,直接按坐标升序排个序,然后看某棵树左右的树会不会被挡住,砍掉相应的高度即可。。 特殊考虑最左边的树和最右边的树。
初始得分100.
第二题:
求从给定的n个数中取一些数(必须取),使得他们的乘积 mod p = c 的方案数。n≤32,p≤10^9,c≤10^9,a[i]<p,p 是质数
解题过程:
1.看到n<=32第一直觉就是 刘汝佳训练指南上提到的 “中途相遇法”, 前dfs 出 前n/2个数的乘积的方案数,用hash 挂链来存。。然后dfs后n/2个数,如果乘积mod p为a, 那么 只要 在前n/2个数中 找到 乘积mod p=x ,a*x mod p = c 的 x的方案数即可。 求解x就是解一个同余方程,可以用拓展欧几里得来实现。。结果考试的时候脑子短路了,怎么都写不出来。。。 (要抽点时间好好整一整它了) 初始得分10分。。
2.其实还有更加简单的方法求x,也是考试之后才想到的。。 根据 p是质数 ,那么 ap-1 ≡ 1 (mod p) 。 同余式两边同时乘c,ap-1 * c ≡ c (mod p)
那么x 就是 ap-2 * c 。 可以证明在p的范围内 x 有唯一解。。 如果 a=0 c!=0当然就无解喽,如果 a=0,c=0 ,那么 前n/2个数不管怎么取都可以,所以方案数就是2n/2 。。其实这是我写题解才发现的 "边界"。。不过不会出现这种情况,题目里有说a[i]<p,所以a[i]不管怎么乘都不会mod p=0,除非a[i]=0。。如果a[i]=0,那么只能说出题人心够狠。。
第三题:
题目描述:有一个英雄,初始生命值是 hp(生命值无上限),在接下来的 n 秒内,每秒会受到一次伤害,第i 秒受到的伤害值为 a[i]。这个英雄 4 有一个道具“魔杖”,魔杖的初始能量为 0,每受到一次伤害,积攒 1 点能量。在英雄受到伤害后,可以立即释放魔棒中的能量,恢复 15×[能量点数]的生命值,且魔棒的点数清零。释放能量有施法间隔 cd(cd 是正整数),即相邻的两次释放的时间间隔至少有 cd 秒。
任何时刻当 hp≤0 时视为死亡,问这个英雄存活下来的前提下, cd 的值最大可以是多少?
如果 cd 没有上限,输出“No upper bound.”;如果无论如何都不能存活,输出-1。 n≤500,|a[i]|≤1000。
解题过程:
1.第二题扩展欧几里得写不来。就跑来搞这题,结果只会用贪心处理 2种无解的情况:
首先要充分理解cd的含义。。如果cd=1,假设第1s末用了魔棒,第2s末就可以再次使用。
对于No upper bound的情况(cd无穷大,那么只有一次用魔棒的机会):只要让英雄不断挨打,如果不加血就要死,那么就让他加血,如果最后还没死,就是No upper bound。如果加血还是要死,那就真的死了。
对于无论如何都不能存活,输出-1的情况:假设cd=1,那么只要每次挨打过后都用魔棒加一次血。如果还是死,那就必死了。
2.对于有解的情况,看看数据范围就知道肯定不是贪心啦。。 至少我只能想到cd一好就用魔棒的贪心策略,但是有谁打dota魔棒cd一好就吃的。。。当然是要留着拿来耍操作 玩极限的喽 ><。 于是就打算二分CD,然后判断能否活下来。
一开始想到用三维的状态来表示,前i分钟,剩余j点能量,还要k分钟才能再次使用魔棒。。。 但是复杂度太高。。 考虑到除了第一次吃魔棒,其他时候吃都必须满足剩余能量大于等于CD。
那么就用F[i][j][0] 表示 到第i分钟末(注意是末)为止,剩余能量为j,且第一次免费用的魔棒还没用,最多还能剩多少血。
对应的F[i][j][1]表示第一次免费魔棒已经用过了。
转移也比较奇葩。
当j>=2的时候 , F[i][j][0]=F[i-1][j-1][0]-cost[i] ; F[i][j][1]=F[i-1][j-1][1]-cost[i];
当j=0的时候,那么第i分钟末肯定吃了一次魔棒;( 因此不存在状态F[i][0][0])
所以 F[i][0][1]=MAX { max{F[i-1][k][0]+15*k } , max{F[i-1][k][1] + 15*k} - cost[i] + 15; +15是因为第i分钟也加了1点能量且在这分钟末被用掉了。 对于第一个max,k无要求,对于第2个条件,k必须大于等于CD,因为前面已经把免费用的魔棒用过了。
那么 就可以用一个g[i-1][0] g[i-1][1] 分别来表示2个max的值了。
同理 F[i][1][1]=MAX { max{F[i-1][k][0]+15*k } , max{F[i-1][k][1] + 15*k} - cost[i] 第i分钟的能量没有用掉,所以不用+15;
或者F[i][1][1]可以直接等于F[i-1][0][1]-cost[i] (也是写题解的时候想到的。应该是对的,明天实验下);
另外f[i][1][0]=f[i-1][0][0]-cost[i]。
所以感觉 不用分j>=2来讨论,直接j>=1的全部归为一种即可。
表达能力有限,贴个代码(只贴判断能否活下来的函数吧),写得很挫。(貌似标准算法是差分约束系统?不过本弱渣不会额。。)
1 bool check(int mid) 2 { 3 memset(f,0,sizeof(f)); 4 memset(g,0,sizeof(g)); 5 6 f[0][0][0]=hp; 7 g[0][0]=hp; 8 9 for (int i=1;i<=n;i++) 10 { 11 for (int j=2;j<=i;j++) 12 { 13 f[i][j][0]=f[i-1][j-1][0]-a[i]; 14 f[i][j][1]=f[i-1][j-1][1]-a[i]; 15 if (f[i][j][0]>0) 16 g[i][0]=max(g[i][0],f[i][j][0]+j*15); 17 if (f[i][j][1]>0 && j>=mid) 18 g[i][1]=max(g[i][1],f[i][j][1]+j*15); 19 } 20 f[i][1][0]=f[i-1][0][0]-a[i]; 21 if (f[i][1][0]>0) 22 g[i][0]=max(g[i][0],f[i][1][0]+15); 23 24 if (max(g[i-1][0],g[i-1][1])>a[i] && max(g[i-1][0],g[i-1][1])>0) 25 f[i][0][1]=max(g[i-1][0],g[i-1][1])+15-a[i]; 26 if (max(g[i-1][0],g[i-1][1])>0) 27 f[i][1][1]=max(g[i-1][0],g[i-1][1])-a[i]; 28 } 29 for (int i=0;i<=n;i++) 30 if (f[n][i][0]>0 || f[n][i][1]>0) 31 return true; 32 return false; 33 }
一开始把CD理解错了,结果答案全部相差1了。。只对了无解的2个点。。20分。 感觉自己还是太弱了。。最后把ans-1去掉就操过去了说。。