题解
E - 激光炸弹
题意
地图上有
n
n
n个目标,用整数
x
i
x_i
xi,
y
i
y_i
yi,表示目标在地图上的位置,每个目标都有一个价值
v
i
v_i
vi。选取一个正方形,边长为
m
m
m,顶点坐标均为整数,且边平行于
x
x
x轴,
y
y
y轴。求出正方形能框选到的最大目标价值和。
1
≤
n
≤
1
0
4
,
0
≤
x
i
,
y
i
≤
5
×
1
0
3
,
1
≤
m
≤
5
×
1
0
3
,
1
≤
v
i
<
100
,确保输出结果不会超过
32767
1\le n \le10^4,0 \le x_i ,y_i \le 5\times 10^3, 1 \le m \le 5\times 10^3,1 \le v_i < 100,确保输出结果不会超过 32767
1≤n≤104,0≤xi,yi≤5×103,1≤m≤5×103,1≤vi<100,确保输出结果不会超过32767
思路
建立一个二维数组
a
[
i
]
[
j
]
a[i][j]
a[i][j]储存在坐标
(
i
,
j
)
(i,j)
(i,j)上所有目标的价值和。然后求出
a
a
a的二维前缀和
s
u
m
sum
sum,递推公式
:
:
:
s
u
m
[
i
]
[
j
]
+
=
s
u
m
[
i
−
1
]
[
j
]
+
s
u
m
[
i
]
[
j
−
1
]
−
s
u
m
[
i
−
1
]
[
j
−
1
]
+
a
[
i
]
sum[i][j]+=sum[i−1][j]+sum[i][j−1]−sum[i−1][j−1] + a[i]
sum[i][j]+=sum[i−1][j]+sum[i][j−1]−sum[i−1][j−1]+a[i]
然后枚举每一个点,计算
:
:
:
m
a
x
(
a
n
s
,
s
u
m
[
i
]
[
j
]
−
s
u
m
[
i
−
m
]
[
j
]
−
s
u
m
[
i
]
[
j
−
m
]
+
s
u
m
[
i
−
m
]
[
j
−
m
]
)
max(ans,sum[i][j] - sum[i - m][j] - sum[i][j - m] + sum[i - m][j - m])
max(ans,sum[i][j]−sum[i−m][j]−sum[i][j−m]+sum[i−m][j−m])
枚举完即可得到答案。
值得注意
x
,
y
x,y
x,y最大为5000,最多只能开一个
500
0
2
5000^2
50002大小的int二维数组,需要把
a
a
a数组和
s
u
m
sum
sum数组合并,且
x
,
y
x,y
x,y可以取
0
0
0,防止数组越界,输入时
x
,
y
x,y
x,y均加一处理。
代码
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n, m;
cin >> n >> m;
int sum[5005][5005] = { 0 };//没有目标地点初始化价值为0
for (int i = 1; i <= n; i++)
{
int x, y, v;
cin >> x >> y >> v;
sum[x + 1][y + 1] = v;//防止越界
}
for (int i = 1; i <= 5001; i++)
for (int j = 1; j <= 5001; j++)
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + sum[i][j];
int ans = 0;
for (int i = m; i <= 5001; i++)
for (int j = m; j <= 5001; j++)
ans = max(ans, sum[i][j] - sum[i - m][j] - sum[i][j - m] + sum[i - m][j - m]);
cout << ans << endl;
return 0;
}
G - Exams
题意
一个人有
m
m
m门科目需要考试,每一门科目需要
a
i
a_i
ai的复习时间(复习时间可以不用连续),并且有一份
n
n
n天的考试安排表,其中
d
i
d_i
di表示第
i
i
i天能考第
i
i
i门科目,假如
d
i
d_i
di为
0
0
0,就代表这一天没有任何科目的考试。试求这个人最少在第几天顺利通过所有考试?(注:这个人一天要么只能考试,要么就只能复习,有考试不可能通过输出
−
1
-1
−1)
1
≤
n
,
m
≤
1
0
5
,
0
≤
d
i
≤
m
,
1
≤
a
i
≤
1
0
5
1 ≤ n, m ≤ 10^5,0 ≤ d_i ≤ m,1 ≤ ai ≤ 10^5
1 ≤ n, m ≤ 105,0 ≤ di ≤ m,1 ≤ ai ≤ 105
思路
首先可以发现对于某一门课目来说,越晚考试,留给我们的复习时间也就越充分,如果这样也有某一门课目过不去,就不能可过去,输出-1即可。
通过上述思考可以发现,我们可以判断在第
k
k
k天,我们能不能通过所有考试。按照这个想法,我们只需要枚举每一天,找到能通过所有考试的天数中的第一天即可得到答案。
但是显然每天都判断一次,时间复杂度为
O
(
n
2
)
O(n^2)
O(n2),会超时,每次判断都能确定最终答案在其左侧或右侧,因此可以使用二分答案的方法,时间复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
代码
#include <bits/stdc++.h>
using namespace std;
int n, m;
int arr[100005];
int Ti[100005];
int check(int sign)
{
int bucket[100005] = { 0 };
int solvearr[100005];
int countm = 0;
for (int i = sign; i >= 1; i--)
{
if (arr[i] != 0 && bucket[arr[i]] == 0)
{
bucket[arr[i]]++;
countm++;
solvearr[i] = arr[i];
}
else
{
solvearr[i] = 0;
}
}
if (countm != m)
return 0;
int count0 = 0;
for (int i = 1; i <= sign; i++)
{
if (solvearr[i] == 0)
count0++;
else
count0 -= Ti[arr[i]];
if (count0 < 0)
return 0;
}
return 1;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> arr[i];
for (int i = 1; i <= m; i++)
cin >> Ti[i];
int R = n, L = 1;
if (!check(n))
{
cout << -1 << endl;
return 0;
}
while (R != L)
{
int mid = (R + L) / 2;
if (check(mid))
R = mid;
else
L = mid + 1;
}
cout << R << endl;
return 0;
}
J - 最大连续和
题意
给你一个长度为
n
n
n的整数序列
A
1
,
A
2
,
⋯
,
A
n
A_1,A_2,\cdots ,A_n
A1,A2,⋯,An ,要求从中找出一段连续的长度不超过
m
m
m 的非空子序列,使得这个序列的和最大。
1
≤
n
,
m
≤
2
×
1
0
5
1≤n,m≤2×10^5
1≤n,m≤2×105
思路
求一段连续子序列的和,可以建立前缀和数组
s
u
m
sum
sum求。考虑到需要找出长度不超过m的和最大的非空子序列,可以用单调队列维护
(
i
−
m
)
(i - m)
(i−m)到
i
−
1
i - 1
i−1这
m
m
m个位置中的
s
u
m
[
i
]
sum[i]
sum[i],
1、维护队首(就是如果队首储存的sum[a]和当前的sum[i]比较a < i - m,那队首就可以被删了)
2、在队尾插入(每插入一个就要从队尾开始往前去判断,如果队尾元素大于
s
u
m
[
i
]
sum[i]
sum[i],那队尾就可以删除)
3、对于
(
i
−
m
+
1
)
(i - m + 1)
(i−m+1)到
i
i
i这
m
m
m个元素,最大序列和为
a
n
s
i
=
s
u
m
[
i
]
−
当前队首元素
ans_i = sum[i] -当前队首元素
ansi=sum[i]−当前队首元素,只需过一遍序列的前缀和求出
m
a
x
(
a
n
s
0
,
a
n
s
1
.
.
.
,
a
n
s
n
)
max(ans_0,ans_1...,ans_n)
max(ans0,ans1...,ansn)即为最终答案。时间复杂度
O
(
n
)
O(n)
O(n)。
代码
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n, m;
int sum[200005] = { 0 };
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
int a;
cin >> a;
sum[i] = sum[i - 1] + a;
}
deque<int> line;
int ans = sum[1];
for (int i = 0; i <= n; i++)
{
while (!line.empty() && i - line.front() > m)//维护队首
line.pop_front();
if(!line.empty())
ans = max(ans, sum[i] - sum[line.front()]);
while (!line.empty() && sum[i] <= sum[line.back()])//维护队尾
line.pop_back();
line.push_back(i);
}
cout << ans << endl;
return 0;
}