1024考试总结
1024考试总结
T1
题目大意:
理想的光滑平面上有\(n\)个加速阶段,每个加速阶段的加速度和持续时间分别是\(a[i], t[i]\),现在让你求一种\(n\)个阶段的排列顺序,使得新的顺序的位移比输入的顺序的位移相差最大,输出最大差值.\(n <= 1e5\).
贪心,按\(a[i]\)从大到小排序会得到最优顺序.
证明用临项交换:假设我们要让先\(i\)后\(j\)比先\(j\)后\(i\)更优,那么:
\(vt_i + \frac{1}{2}a_it_i^2 + (v + a_it_i)t_j + \frac{1}{2} a_jt_j^2 > vt_j + \frac{1}{2}a_jt_j^2 + (v + a_jt_j)t_i + \frac{1}{2} a_it_i^2\)
化简一下就可得到:\(a_i > a_j\).
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 1e5 + 5;
int n;
struct node { int a, t; } x[N], y[N];
int cmp(node x, node y) { return x.a > y.a; }
long double calc() {
long long vx = 0, vy = 0; long double res = 0;
for(int i = 1;i <= n; i++) {
res += (1ll * vy * y[i].t + 1ll * y[i].a * y[i].t * y[i].t / 2) - (1ll * vx * x[i].t + 1ll * x[i].a * x[i].t * x[i].t / 2);
vx += 1ll * x[i].a * x[i].t; vy += 1ll * y[i].a * y[i].t;
}
return res;
}
int main() {
n = read();
for(int i = 1;i <= n; i++) y[i].a = x[i].a = read(), y[i].t = x[i].t = read();
sort(y + 1, y + n + 1, cmp);
printf("%.1Lf", calc());
return 0;
}
T2
题目大意:
一个人买咖啡,他需要每个小时都喝一杯热咖啡,共有\(n\)小时,现在每个小时的咖啡价格不同,一杯咖啡可以持续热\(h\)个小时,可以同一个小时买多杯咖啡留给后面喝(只要没有凉就行),问怎么买咖啡可以使总钱数最小,输出从\(b\)到\(e\)小时内每个小时买了几杯咖啡.\(n <= 1e5\)
假设我们现在在\(i\)小时,这个小时喝的咖啡肯定是在这个时间段买的:\([i - h + 1, i]\).所以我们要找到这个时间段的最小值的位置,在这个位置给\(i\)位置买一杯咖啡.这样可以花钱最少.所以维护一个单调队列就好了.
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 1e5 + 5;
int n, h, b, e;
int a[N], q[N], ans[N];
int main() {
while(cin >> n >> h >> b >> e) {
for(int i = 1;i <= n; i++) a[i] = read(), ans[i] = 0;
int l = 1, r = 0;
for(int i = 1;i <= n; i++) {
while(l <= r && q[l] <= i - h) l ++;
while(l <= r && a[q[r]] >= a[i]) r --;
q[++ r] = i;
ans[q[l]] ++;
}
for(int i = b;i <= e; i++) printf("%d ", ans[i]);
printf("\n");
}
return 0;
}
T3
题目链接
题目大意:给你一维,二维,三维空间,再给\(n\)个点,让你求有多少个点对曼哈顿距离不超过\(d\).
一维:直接双指针乱搞一下.
二维:曼哈度距离转切比雪夫距离,\((x, y) -> (x + y, x - y)\),新坐标系里的切比雪夫距离就是原坐标系里的曼哈顿距离.首先按\(x\)从小到大排序,然后暴力枚举\(x\).\(y\)用数据结构维护一下.
三维:还是曼哈顿距离转切比雪夫距离.新增了一维,但是每个点的坐标范围变成了\(1 <= x,y,z <= 75\).我们可以将z看做层数,计算每一层和层与层之间的答案.这样只用把\(x,y\)转化成切比雪夫距离就好了.然后我们每层搞一个二维前缀和.查询的时候可以\(O(1)\)查询.这里不同层的答案和相同层的答案分开处理比较好,因为相同层的答案会算重复.
#include <bits/stdc++.h>
#define ls(o) (o << 1)
#define rs(o) (o << 1 | 1)
#define mid ((l + r) >> 1)
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 1e5 + 5;
int B, n, d, m;
int sum[80][150][150];
long long ans, ans1;
struct node { int x, y, z; } a[N];
struct tree { int sum; } t[N << 4];
int cmp(node a, node b) { return a.x < b.x; }
int dis(int i, int j) {
if(B == 2) return abs(a[i].x - a[j].x) + abs(a[i].y - a[j].y);
if(B == 3) return abs(a[i].x - a[j].x) + abs(a[i].y - a[j].y) + abs(a[i].z - a[j].z);
}
void work_B_1() {
sort(a + 1, a + n + 1, cmp);
int last = 1, tmp;
for(int i = 1;i <= n; i++) {
int j;
for(j = last;j <= n; j++)
if(a[j].x - a[i].x > d) { last = j - 1; break; }
ans += j - i - 1;
}
printf("%lld", ans);
}
void up(int o) {
t[o].sum = t[ls(o)].sum + t[rs(o)].sum;
}
void insert(int o, int l, int r, int x, int f) {
if(l == r) { t[o].sum += f; return ; }
if(x <= mid) insert(ls(o), l, mid, x, f);
if(x > mid) insert(rs(o), mid + 1, r, x, f);
up(o);
}
int query(int o, int l, int r, int x, int y) {
if(x <= l && y >= r) { return t[o].sum; }
int res = 0;
if(x <= mid) res += query(ls(o), l, mid, x, y);
if(y > mid) res += query(rs(o), mid + 1, r, x, y);
return res;
}
void work_B_2() {
for(int i = 1;i <= n; i++) {
int x = a[i].x, y = a[i].y;
a[i].x = x + y; a[i].y = x - y;
}
sort(a + 1, a + n + 1, cmp);
int last = 1; insert(1, -m, m, a[1].y, 1);
for(int i = 2;i <= n; i++) {
while(last < i && a[i].x - a[last].x > d)
insert(1, -m, m, a[last].y, -1), last ++;
ans += query(1, -m, m, a[i].y - d, a[i].y + d);
insert(1, -m, m, a[i].y, 1);
}
printf("%lld", ans);
}
void work_B_3() {
for(int i = 1;i <= n; i++) {
int x = a[i].x, y = a[i].y;
a[i].x = x + y; a[i].y = x - y + m;
sum[a[i].z][a[i].x][a[i].y] ++;
}
for(int i = 1;i <= m; i++)
for(int j = 1;j <= 2 * m; j++)
for(int k = 1;k <= 2 * m; k++) sum[i][j][k] += sum[i][j - 1][k] + sum[i][j][k - 1] - sum[i][j - 1][k - 1];
for(int i = 1;i <= n; i++) {
for(int j = max(1, a[i].z - d);j < a[i].z; j++) {
int D = d - (a[i].z - j);
int x1 = max(1, a[i].x - D), x2 = min(2 * m, a[i].x + D);
int y1 = max(1, a[i].y - D), y2 = min(2 * m, a[i].y + D);
ans += sum[j][x2][y2] - sum[j][x1 - 1][y2] - sum[j][x2][y1 - 1] + sum[j][x1 - 1][y1 - 1];
}
int D = d;
int x1 = max(1, a[i].x - D), x2 = min(2 * m, a[i].x + D);
int y1 = max(1, a[i].y - D), y2 = min(2 * m, a[i].y + D);
ans1 += sum[a[i].z][x2][y2] - sum[a[i].z][x1 - 1][y2] - sum[a[i].z][x2][y1 - 1] + sum[a[i].z][x1 - 1][y1 - 1];
}
printf("%lld", ans + (ans1 - n) / 2);
}
int main() {
B = read(); n = read(); d = read(); m = read();
if(B == 1) for(int i = 1;i <= n; i++) a[i].x = read();
if(B == 2) for(int i = 1;i <= n; i++) a[i].x = read(), a[i].y = read();
if(B == 3) for(int i = 1;i <= n; i++) a[i].x = read(), a[i].y = read(), a[i].z = read();
if(B == 1) { work_B_1(); }
if(B == 2) { work_B_2(); }
if(B == 3) { work_B_3(); }
return 0;
}