题意:
Vanya要堆金字塔,第1层要1个方块,第2层要1+2个,第3层要1+2+3个,以此类推。现在Vanya有n个方块,求能堆的金字塔的最大高度。
n < 10 ^ 4。
题解:
设金字塔高度为h,则需要的积木数为\sum_{i = 1} ^ {h} {\sum_{j = 1} ^ {i} {j}} = \sum_{i = 1} ^ h {i * (h + 1 - i)} = (h + 1) * \sum_{i = 1} ^ h {i} - \sum_{i = 1} ^ h {i ^ 2} = h * (h + 1) * (h + 2) / 6,h大约为O(n ^ (1 / 3))级别,直接枚举即可,不写公式也可以。
时间复杂度O(n ^ (1 / 3))。
代码:
#include <cstdio> int n, a, b, c; int main() { scanf("%d", &n); while(c <= n) { ++a; b += a; c += b; } printf("%d\n", --a); return 0; }
题意:
有一个数轴,数轴上区间[0, l]中有n个灯笼,对于一个给定的半径D,一个灯笼若在x可以照亮的范围是[x - D, x + D],请你求出最小的D,使得区间[0, l]每一点至少被一个灯笼点亮,答案误差不超过10^(-9)。
n <= 1000, l <= 10 ^ 9。
题解:
实际上我们只用考虑相邻的两个灯笼可以笼罩的范围,相邻的两个灯笼必须能笼罩二者之间的每一个点,不妨对灯笼按坐标排序,考虑任意两个相邻的灯笼距离为k,则2*D>=k,再考虑上端点附近的灯笼必须笼罩端点即可。(精度问题不是问题)
时间复杂度O(nlogn)。
代码:
#include <cstdio> #include <algorithm> using namespace std; int n, l, a[2333]; double ans; int main() { scanf("%d%d", &n, &l); for(int i = 0; i < n; ++i) scanf("%d", a + i); sort(a, a + n); ans = max(a[0], l - a[n - 1]); for(int i = 1; i < n; ++i) ans = max(ans, (a[i] - a[i - 1]) / 2.0); printf("%.10f\n", ans); return 0; }
题意:
Vanya希望通过n场考试并顺利拿到学位,已知通过的要求是n场考试平均分超过avg,每场考试得分不得超过r,现在Vanya在每场考试已有ai点成绩,如果想相应地提升1分,需要多写bi个文章,求拿到学位最少需要多写多少文章。
n <= 10 ^ 5。
题解:
因为1分对应一些文章,所以我们可以考虑贪心解决,首先可以算出来还需要提升的总点数,不妨将考试按bi排序,按照bi从小到大选择还没有到达最高分的考试进行补助,算出的答案必定是最少的。
时间复杂度O(nlogn)。
代码:
#include <cstdio> #include <algorithm> using namespace std; int n, r, avg; long long res, ans; struct Node { int a, b; bool operator < (const Node &x) const { return b < x.b; } } e[233333]; int main() { scanf("%d%d%d", &n, &r, &avg); for(int i = 0; i < n; ++i) { scanf("%d%d", &e[i].a, &e[i].b); res += avg - e[i].a; } sort(e, e + n); for(int i = 0; i < n && res > 0; ++i) if(r - e[i].a > 0) { long long k = min(res, (long long)r - e[i].a); res -= k; ans += k * e[i].b; } printf("%I64d\n", ans); return 0; }
题意:
Vanya和他的好友Vova开始玩打怪游戏,怪物有n只,每只有ai点血量,角色打一下怪物可以使怪物掉1点血,Vanya每1/x秒会打一下怪物,而Vova则是每1/y秒,Vanya想知道每只怪物是谁最后打死的,以便计分。(考虑同时打中)
n <= 10 ^ 5, x, y <= 10 ^ 6, ai<= 10 ^ 9。
题解:
其实可以将题目转化为,Vanya每y秒打一下怪物,而Vova每x秒打一下怪物,这样结算怪物是谁打死的结果不会变,则我们可以二分预测是第几秒恰好打死怪物,从而判断最终杀怪属于谁。
代码:
#include <cstdio> int n, x, y, a; int main() { scanf("%d%d%d", &n, &x, &y); while(n--) { scanf("%d", &a); long long L = 0, R = 1e15, M; while(L < R) { M = L + R >> 1; if(M / x + M / y < a) L = M + 1; else R = M; } if(L % x == 0 && L % y == 0) puts("Both"); else puts(L % y == 0 ? "Vanya" : "Vova"); } return 0; }
题意:
有一个二维循环空间,横纵坐标的取值范围均为[0,n),现在空间里有m个点,规定一个运动向量{dx, dy},运动向量描述的是从(x, y)到((x + dx) mod n, (y + dy) mod n)的运动,请你选择一个起点,使得按照运动向量运动回这个点时经过的点数最多。
n <= 10 ^ 6, m <= 10 ^ 5。
题解:
对于一个(x, y)来说,它和((x + dx) mod n, (y + dy) mod n)、((x + 2 * dx) mod n, (y + 2 * dy) mod n)……((x + (n - 1) * dx) mod n, (y + (n - 1) * dy) mod n)都在同一个运动轨迹上,不妨从中选出一个横坐标为0的点,则其他的点也可以用这个点得出。那么当横坐标为0,纵坐标不同时,其实描述的是不同起点但是相似的运动轨迹。所以可以预先处理出运动轨迹关于y的函数,然后将那m个点转化为横坐标为0的点进行统计,从而得出答案。
时间复杂度O(n + m)。
代码:
#include <cstdio> int n, m, dx, dy, f[2333333], cnt[2333333], ans[3]; int main() { scanf("%d%d%d%d", &n, &m, &dx, &dy); for(int i = 0, x = 0, y = 0; i < n; ++i) { f[x] = y; x += dx; if(x >= n) x -= n; y += dy; if(y >= n) y -= n; } for(int x, y; m; --m) { scanf("%d%d", &x, &y); y -= f[x]; if(y < 0) y += n; ++cnt[y]; if(ans[0] < cnt[y]) { ans[0] = cnt[y]; ans[1] = x; ans[2] = y + f[x]; if(ans[2] >= n) ans[2] -= n; } } printf("%d %d\n", ans[1], ans[2]); return 0; }
人学多了总是容易想复杂,要及时复习总结;阅读能力有待加强,手速有待加强。