不用C写,练练Python=w=
cf link
Problem A
复杂度没要求,直接枚举
Problem B
给定数组a, 每次能对 a_i, a_i-1, a_i+1 进行减法。问是否能使得数组为变为0。从前往后模拟减法操作即可,最后判断数组是否全为0.
def is_all_zero(arr):
return all(element == 0 for element in arr)
def solve():
n = int(input())
# n = map(int, input().strip().split())
a = list(map(int, input().strip().split()))
for i in range(1, n - 1):
val = min(a[i - 1], a[i] // 2, a[i + 1])
a[i - 1] -= val
a[i] -= val * 2
a[i + 1] -= val
if is_all_zero(a):
flag = True
else:
flag = False
ans = "YES" if flag == True else "NO"
print(ans)
if __name__ == "__main__":
t = int(input())
while t > 0:
t -= 1
solve()
C
描述:给定一个字符串,你可以删除任意位置的字符。要求用最少的删除次数,使得字符串不包含“map”和“pie”的子串。通过样例显而易见,对于一般的情况,对于一个子串map或pie,删除其中一个字符即可。“mapie”这种情况则只需要删除中间的p。所以特判下“mapie”即可。
def solve():
n = int(input())
s = input()
ans = 0
i = 0
while i < n:
# print("i: ", i)
if i + 5 <= n and s[i : i + 5] == "mapie":
ans += 1
# print(i, " case1")
i += 5
continue
if i + 3 <= n and (s[i : i + 3] == "map" or s[i : i + 3] == "pie"):
ans += 1
# print(i, " case2")
i += 3
continue
i += 1
print(ans)
if __name__ == "__main__":
t = int(input())
while t > 0:
t -= 1
solve()
D-简单dp
描述:1—n个人在环上,开始时,编号为x的人拿球。给定一系列操作,拿球的人可以顺时针或逆时针传球一定的距离d给别人,这个操作也可能是?,也就是两种状态都要考虑。直接考虑dp,dp[i][j]表示第i轮第j个人是否有可能拿到球。根据操作符为0,1,?分情况转移。最后把第n轮的所有可能为1的人输出。
def solve():
n, m, x = tuple(map(int, input().strip().split()))
x = (x - 1 + n) % n
dp = [ [0] * n for _ in range(m + 1) ]
dp[0][x] = 1
ans = []
for i in range(m):
input_string = input()
dist, c = int(input_string.split()[0]), input_string.split()[1]
# print("dist: ", dist, " c: ", c)
for j in range(n):
if dp[i][j] == 0:
continue
# print("i: ", i, " j: ", j)
if c == '?':
dp[i + 1][(j + dist) % n] = 1
dp[i + 1][(j + n - dist) % n] = 1
elif c == '0':
dp[i + 1][(j + dist) % n] = 1
elif c == '1':
dp[i + 1][(j + n - dist) % n] = 1
for i in range(n):
if dp[m][i] == 1:
ans.append(i + 1)
print(len(ans))
for i in range(len(ans)):
if i != len(ans) - 1:
print(ans[i], end=' ')
else:
print(ans[i], end='\n')
if __name__ == "__main__":
t = int(input())
while t > 0:
t -= 1
solve()
E-区间查询+前缀和(C++)
描述:给定一个nxm的二维矩阵,一个值k。要选择连续的k行,在这些行上分别建桥。对于每一行,第一列和最后一列必须垫support, 建桥的代价为垫的a(i,j)+1,还要求垫的support之间的距离不能超过d。
假设我们就处理一行,就有m个位置,dp[i]表示搭到第i个位置时的最小代价。由于距离d的限制,dp[i]可以从dp[i - d - 1 ~ i - 1]转移过来,也就是查询区间的最小值。这个查询操作可以通过线段树或multiset之类的实现。dp[m]即为这一行的最小代价。
现在得到每一行的最小代价后,求连续k行的最小代价,只需要再做个前缀和即可。
Python超时了,典中典
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
#define ll long long
#define inf LLONG_MAX / 2
struct Node {
ll l, r, val, lazy;
};
struct SegmentTree {
vector<Node> tree;
SegmentTree(int n) {
tree.resize(n * 4);
}
int left_child(int rt) {
return rt << 1;
}
int right_child(int rt) {
return (rt << 1) | 1;
}
void push_up(int rt) {
tree[rt].val = min(tree[left_child(rt)].val, tree[right_child(rt)].val);
}
void push_down(int rt) {
if (tree[rt].lazy == 1) {
tree[left_child(rt)].lazy ^= 1;
tree[right_child(rt)].lazy ^= 1;
tree[left_child(rt)].val = min(tree[left_child(rt)].val, tree[rt].val);
tree[right_child(rt)].val = min(tree[right_child(rt)].val, tree[rt].val);
tree[rt].lazy = 0;
}
}
void build(int l, int r, int rt) {
tree[rt].l = l;
tree[rt].r = r;
tree[rt].lazy = 0;
if (l == r) {
tree[rt].val = inf;
return;
}
int mid = (l + r) >> 1;
build(l, mid, left_child(rt));
build(mid + 1, r, right_child(rt));
push_up(rt);
}
void update(int l, int r, int rt, ll value) {
if (tree[rt].l > r || tree[rt].r < l) {
return;
}
if (l <= tree[rt].l && tree[rt].r <= r) {
tree[rt].val = min(value, tree[rt].val);
tree[rt].lazy ^= 1;
return;
}
push_down(rt);
if (tree[rt].r >= l) {
update(l, r, left_child(rt), value);
}
if (tree[rt].l <= r) {
update(l, r, right_child(rt), value);
}
push_up(rt);
}
ll query(int l, int r, int rt) {
if (tree[rt].l > r || tree[rt].r < l) {
return inf;
}
if (l <= tree[rt].l && tree[rt].r <= r) {
return tree[rt].val;
}
push_down(rt);
ll ans = inf;
if (tree[rt].r >= l) {
ans = min(ans, query(l, r, left_child(rt)));
}
if (tree[rt].l <= r) {
ans = min(ans, query(l, r, right_child(rt)));
}
push_up(rt);
return ans;
}
};
void solve() {
int n, m, k, d;
cin >> n >> m >> k >> d;
vector<vector<int>> grid(n, vector<int>(m));
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
cin >> grid[i][j];
}
}
vector<ll> pre(n + 1, 0);
SegmentTree segTree(m);
for (int i = 0; i < n; ++i) {
grid[i].insert(grid[i].begin(), 0);
segTree.build(1, m, 1);
segTree.update(1, 1, 1, 1);
for (int j = 2; j <= m; ++j) {
ll preVal = segTree.query(max(1, j - d - 1), max(1, j - 1), 1);
ll curVal = segTree.query(j, j, 1);
// cout << "j: " << j << " curVal: " << curVal << " preVal: " << preVal << " grid: " << grid[i][j] << "\n";
if (preVal + grid[i][j] + 1 < curVal) {
segTree.update(j, j, 1, preVal + grid[i][j] + 1);
}
}
pre[i + 1] = segTree.query(m, m, 1);
}
ll ans = inf;
for (int i = 0; i <= n; ++i) {
pre[i] += pre[i - 1];
}
for (int i = k; i <= n; ++i) {
ans = min(ans, pre[i] - pre[i - k]);
}
cout << ans << endl;
}
int main() {
int t;
cin >> t;
while (t > 0) {
t--;
solve();
}
return 0;
}
F-二分+二分
描述:给定数组a,d,f。定义
d
i
f
f
=
m
a
x
(
a
i
−
a
i
−
1
)
,
2
<
=
i
<
=
n
diff = max (\:a_i - a_{i-1}), 2 <= i <= n
diff=max(ai−ai−1),2<=i<=n,也即相邻元素的最大差值。你可以分别选择d,f中的一个下标i,j。将
d
i
+
f
j
d_i + f_j
di+fj插入到数组a(此时数组a仍是升序)。最多只能操作一次,也可以不操作。要求操作后最小化 这个差值
d
i
f
f
diff
diff。
思路:记a中的最大差值为
m
a
x
_
d
i
f
f
max\_diff
max_diff,第二大的差值为
s
e
c
_
d
i
f
f
sec\_diff
sec_diff。
若
m
a
x
_
d
i
f
f
=
=
s
e
c
_
d
i
f
f
max\_diff == sec\_diff
max_diff==sec_diff,实际上题目所给的操作只能变化一个diff,此时无论怎么添加元素都不改变结果。
考虑
m
a
x
_
d
i
f
f
!
=
s
e
c
_
d
i
f
f
max\_diff != sec\_diff
max_diff!=sec_diff,此时最大差值只有一个,它所在的下标已知,记为
a
i
,
a
i
+
1
,
(
i
=
m
a
x
_
i
d
x
)
a_i, a_{i+1}, (i = max\_idx)
ai,ai+1,(i=max_idx)。
最小化最大值这种策略,通常可以二分,二分答案。假如此时二分的差值为mid,,若能通过操作使得数组最大差值小于等于mid,则降低上界,记录结果,否则提高下界。
对于得到最大差值的两个数
[
a
i
,
a
i
+
1
]
[a_i,a_{i+1}]
[ai,ai+1],我们要保证插入的数值范围落在
[
a
i
+
1
−
m
i
d
,
a
i
+
m
i
d
]
[a_{i+1} - mid, a_i + mid]
[ai+1−mid,ai+mid]且
a
i
+
1
−
m
i
d
<
=
a
i
+
m
i
d
a_{i+1} - mid <= a_i + mid
ai+1−mid<=ai+mid。
有插入的值范围
[
a
i
+
1
−
m
i
d
,
a
i
+
m
i
d
]
[a_{i+1} - mid, a_i + mid]
[ai+1−mid,ai+mid], 暂时记作
[
L
,
R
]
[L,R]
[L,R]。那么枚举数组d,对于每个d[i], 二分查询d的大于等于L-d[i]的lower_bound,得到的下标为
l
e
f
t
_
i
d
x
left\_idx
left_idx。
注意,我们只需要判断是否有数值落在区间内,不需要知道具体是哪些,当
f
[
l
e
f
t
_
i
d
x
]
+
d
[
i
]
>
=
L
a
n
d
f
[
l
e
f
t
_
i
d
x
]
+
d
[
i
]
<
=
R
f[left\_idx] + d[i] >= L \quad and\quad f[left\_idx] + d[i] <= R
f[left_idx]+d[i]>=Landf[left_idx]+d[i]<=R时,说明有数值在这个区间内,check为True。还有些细节见代码…
import bisect
def check(mid, max_idx, a, d, f, n, m, k):
left_border, right_border = a[max_idx], a[max_idx + 1]
L = right_border - mid
R = left_border + mid
if L > R:
return False
for i in range(1, m + 1):
left_idx = bisect.bisect_left(f, L - d[i])
# right_idx = bisect.bisect_right(f, R - d[i])
# if f[right_idx] + d[i] > R:
# right_idx -= 1
# print("i: ", i,"left_idx: ", left_idx, " right_idx: ", right_idx)
if left_idx == 0:
left_idx += 1
if left_idx == k + 1:
left_idx -= 1
if f[left_idx] + d[i] >= L and f[left_idx] + d[i] <= R:
return True
return False
def solve():
n, m, k = tuple(map(int, input().strip().split()))
a = list(map(int, input().strip().split()))
max_diff, sec_diff, max_idx = -1, -1, 0
for i in range(1, n):
if max_diff < a[i] - a[i - 1]:
sec_diff = max_diff
max_diff = a[i] - a[i - 1]
max_idx = i - 1
elif max_diff == a[i] - a[i - 1]:
sec_diff = max_diff
elif sec_diff < a[i] - a[i - 1]:
sec_diff = a[i] - a[i - 1]
d = list(map(int, input().strip().split()))
d.insert(0, 0)
d.append(4e9 + 1)
d.sort()
f = list(map(int, input().strip().split()))
f.insert(0, 0)
f.append(4e9 + 1)
f.sort()
if max_diff == sec_diff and sec_diff != -1:
print(max_diff)
return
l, r, ans = max(1, sec_diff), max_diff, max_diff
while l <= r:
mid = (l + r) // 2
# print("mid: ", mid)
if check(mid, max_idx, a, d, f, n, m, k):
r = mid - 1
ans = mid
# print("passed")
else:
l = mid + 1
# print("rejected")
print(ans)
if __name__ == "__main__":
t = int(input())
while t > 0:
t -= 1
solve()
G
G题本来想从图重建一个颜色的图,再BFS求最短距离。结果发现这样重建复杂度超了。原题解直接把颜色连到原图中,答案除2即可。(我太笨了)