【Codeforce 933 Div.3】练练Python=w=

不用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(aiai1),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+1mid,ai+mid] a i + 1 − m i d < = a i + m i d a_{i+1} - mid <= a_i + mid ai+1mid<=ai+mid
有插入的值范围 [ a i + 1 − m i d , a i + m i d ] [a_{i+1} - mid, a_i + mid] [ai+1mid,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即可。(我太笨了)

  • 31
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值