首页 > 代码库 > 二模 (7) day1

二模 (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 }
View Code

 

一开始把CD理解错了,结果答案全部相差1了。。只对了无解的2个点。。20分。 感觉自己还是太弱了。。最后把ans-1去掉就操过去了说。。

 

二模 (7) day1