CH0101_a^b
描述
求a的b次方对p取模的值
数据范围
1 ≤ a, b, p ≤ 10^9
思路
快速幂
解决
1 |
|
反思和坑
快速幂是可以处理幂指数是0的情况的,只需要把ret初值设置为1,哪怕一次计算也不进行,结果仍然是正确的。
但有一种情况十分特殊,即指数是0和模数为1,由于指数是0会跳过快速幂的循环,进而也无法取模,导致结果为错误的1。
所以,在输出时要再次加上取模操作。
POJ2018_Best Cow Fences
描述
John的农场包括N块场地,每个场地里有一些牛。
John想要建一个栅栏把这些场地圈起来,如果一些场地被圈在了同一个栅栏内,它们就组成了一个区块,区块至少需要包含F个场地,需要注意的是,只有相邻的场地才能被圈在一起。John想知道不同的圈地方案里,能得到的区块里牛相对于每个场地的最大平均数目是多少。
数据范围
1 <= N <= 100,000
1 <= ncows <= 2000
1 <= F <= N
ncows表示每个场地的牛数目。
输出格式
输出答案乘以1000,只取整数部分。
思路
二分答案,答案最大不会超过牛最多的场地里牛个数,最小不会小于0,这样就得到了初始的上下界。
我使用的是整数二分,即直接乘以1000,这样做需要注意一些细节。
如何判断某个解m是否可行,我们只需要看数组里面有没有长度大于F的,连续的子序列,其平均值大于m。
但这个判断条件还是很抽象,我们不妨预先给数组里的每个数减去m,判断连续的子序列之和是不是大于等于0就行了。
为了便于得到连续的子序列和,我们还需要计算一些前缀和。
这样,在找子序列的时候,我们从下标F-1开始,遍历子序列的终点i,起点取下标是0~i-F中,前缀和最小的一个,这样可以保证我们每次找到的都是最大的,同时也节省了时间。
需要注意的细节有:
由于m是乘了1000的,所以计算时数组也不要忘记了乘1000。
前缀和要用long long,其他的数不妨也用。
整数二分时,注意边界。
解决
1 | #include <cstdio> |
注
这题虽然简单,但我调了4个小时,看着代码调,到挑不出错又自己写了个随机数产生器,找了个标程对拍,最终发现对于子序列终点大于F-1的,都没考虑起点是0的情况。
还是太菜了。
POJ2456_Agressive Cows
描述
Farmer John 有一个很长的牛棚,他把牛棚分成了N个隔间,每个隔间位于xi的位置。
John的C头牛不喜欢这样的牛棚布局,所以它们变得极具攻击性,John希望把这些牛尽量分散,以防止它们相互攻击。
计算出两头牛之间最小距离的最大值。
数据范围
2 <= N <= 100,000
0 <= xi <= 1,000,000,000
2 <= C <= N
思路
二分答案。
解决
1 | #include <cstdio> |
三分求单峰/谷函数极值
观察函数图像:f(x) = xlnx
假设我们现在要取该函数的极小值点,且知道极小值点在(l, r)之间,我们取两个三分点lmid和rmid,f(lmid)和f(rmid)的关系,有几种情况
1 f(rmid) > f(lmid)如图:
此时极值点可以有两种情况,要么在lmid和rmid之间,要么在l和lmid之间,不可能在rmid和r之间,否则从l到lmid再到rmid形成了一个递减区间,这样就不会有f(rmid) > f(lmid)。
因此,我们可以把区间缩为[l, rmid],继续三分。
2 f(lmid) > f(rmid),同理,区间缩为[lmid, r]。
就以f(x) = xlnx 为例,我们假设知道其极小值点在区间[0, 1000]上,现在在精度要求为1e-4的情况下找到其极小值点,解法为:
#include
#include
using namespace std;
const double eta = 1e-4;
double f(double x){
return x * log(x);
}
int main(){
double l = 0;
double r = 1000;
double lm = (l 2 + r) / 3;
double rm = (l + r 2) / 3;
while(r - l > eta){
if(f(lm) > f(rm)){
l = lm;
}else{
r = rm;
}
lm = (l 2 + r) / 3;
rm = (l + r 2) / 3;
}
printf(“%lf\n”, l);
}
计算出的结果是0.367832等于1/e,这样的时间复杂度是O(logn)