Codeforces Round 530 (Div. 2)

写在前面

比赛地址:https://codeforces.com/contest/1099

睡不着又不知道玩什么随便开了把。

19 年的怎么这么水感觉和最近的难度差好多呃呃

A

签到。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int w, h; std::cin >> w >> h;
  int u1, d1, u2, d2; std::cin >> u1 >> d1 >> u2 >> d2;

  LL s = w;
  for (int i = h; i >= 0; -- i) {
    s += i;
    if (i == d1) s = std::max(0ll, s - u1);
    if (i == d2) s = std::max(0ll, s - u2);
  }
  std::cout << s << "\n";
  return 0;
}

B

签到。

周长相等的矩形中正方形的面积最大,确定了矩形的形态后仅需将画出一组长和高即可,答案即 \(w+h\)

\(s = \sqrt{n}\),显然 \(s\times s\) 的矩形一定合法,检查 \((s-1)\times s\) 的矩形是否合法即可。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
//=============================================================
int main() {
//   freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int n; std::cin >> n;
  int s = ceil(sqrt(n));
  if (s * (s - 1) >= n) {
    std::cout << s + s - 1 << "\n";
  } else {
    std::cout << 2 * s << "\n";
  }
  return 0;
}

C

签到。

\(k>n\) 则字符串中至少有一个 *,若 \(k<n\) 则特殊字符数不小于 \(n-k\)

模拟即可。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  std::string s; std::cin >> s;
  int k; std::cin >> k;

  int cntp1 = 0, cntp2 = 0;
  for (int i = 0, len = s.length(); i < len; ++ i) {
    if (s[i] == '?') ++ cntp1;
    if (s[i] == '*') ++ cntp2;
  }

  int true_len = s.length() - cntp1 - cntp2;
  if (k > true_len && !cntp2) {
    std::cout << "Impossible";
    return 0;
  } else if (k < true_len && true_len - k > cntp1 + cntp2) {
    std::cout << "Impossible";
    return 0;
  }

  std::string ans;
  if (k > true_len) { 
    for (int i = 0, len = s.length(); i < len; ++ i) {
      if (s[i] == '*') {
        while (true_len < k) ans.push_back(s[i - 1]), ++ true_len;
      } else if (s[i] != '?') {
        ans.push_back(s[i]);
      }
    }
  } else {
    for (int i = 0, len = s.length(); i < len; ++ i) {
      if (s[i] == '?' || s[i] == '*') {
        if (true_len > k) ans.pop_back(), -- true_len;
      } else {
        ans.push_back(s[i]);
      }
    }
  }
  std::cout << ans << "\n";
  return 0;
}

D

构造。

若树不合法说明每种满足给定条件的构造,均会使某节点的权值变为负数。

为了在给定的 \(s\) 的限制下最小化 \(\sum_u a_u\),一个显然的想法是令分叉点 \(u\)\(a_u\) 尽可能大,从而更多地在其子节点的 \(s_v\) 中复用 \(a_u\) 的贡献。则对于所有未知权值的节点 \(u\),在保证 \(a_u\ge 0\) 时最优的构造是:

\[s_u = \begin{cases} \min_{v\in \operatorname{son}(u)} s_v &(\operatorname{son}(u) \not= \empty)\\ s_{\operatorname{fa}(u)} &\text{otherwise} \end{cases}\]

DFS 构造出 \(s\) 后再差分检查 \(a\) 是否非负即可。

//构造
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 1e5 + 10;
const int kInf = 1e9 + 2077;
//=============================================================
int n, fa[kN], s[kN];
int edgenum, head[kN], v[kN << 1], ne[kN << 1];
bool flag = 1;
LL ans;
//=============================================================
void Add(int u_, int v_) {
  v[++ edgenum] = v_;
  ne[edgenum] = head[u_];
  head[u_] = edgenum;
}
void Dfs1(int u_, int fa_) {
  int minn = kInf;
  for (int i = head[u_]; i; i = ne[i]) {
    Dfs1(v[i], u_);
    minn = std::min(minn, s[v[i]]);
  }
  if (s[u_] == -1) s[u_] = ((minn == kInf) ? s[fa_] : minn);
}
void Dfs2(int u_, int fa_) {
  ans += s[u_] - s[fa_];
  if (s[u_] < s[fa_]) flag = 0;
  for (int i = head[u_]; i; i = ne[i]) Dfs2(v[i], u_);
}
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  std::cin >> n;
  for (int i = 2; i <= n; ++ i) {
    int f; std::cin >> f;
    Add(f, i);
  }
  for (int i = 1; i <= n; ++ i) std::cin >> s[i];
  Dfs1(1, 0), Dfs2(1, 0);
  if (!flag) ans = -1;
  std::cout << ans << "\n";
  return 0;
}

E

结论,码农

基于小结论的呃呃码农提。

发现对于所有的合法矩阵,只有两种情况:

  • 每行均只有两种字符,奇偶行上的字符种类互补,且每行的形态只有 ABABABABBABABABA 两种形态,且各行独立。
  • 每列均只有两种字符,奇偶列上的字符种类互补,且每列的形态只有 ABABABABBABABABA 两种形态,且各列独立。

于是先考虑第一种情况,枚举字符种类后按行枚举找各行的最少修改数贪心修改,然后转下矩阵重复即可。

妈的码农

//结论,码农
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
char ch[5] = "AGCT";
int n, m, type, minsum;
std::string seed;
std::vector <std::string> a, ans;
//=============================================================
void Init() {
  std::cin >> n >> m;
  minsum = n * m;
  for (int i = 0; i < n; ++ i) {
    std::string s; std::cin >> s;
    a.push_back(s);
  }
}
void checka(std::string s_) {
  for (int i = 0; i < 4; ++ i) {
    for (int j = 0; j < i; ++ j) {
      if (s_[i] == s_[j]) return ;
    }
  }

  int sum = 0;
  for (int i = 0; i < n; ++ i) {
    int s1 = 0, s2 = 0;
    std::string t1 = s_.substr(2 * (i % 2 == 1), 2);
    std::string t2 = t1.substr(1, 1) + t1.substr(0, 1);
    for (int j = 0; j < m; j += 2) {
      for (int k = j, l = 0; k <= std::min(j + 1, m - 1); ++ k, ++ l) {
        if (a[i][k] != t1[l]) ++ s1;
        if (a[i][k] != t2[l]) ++ s2;
      }
    }
    sum += std::min(s1, s2);
  }
  if (sum < minsum) type = 0, minsum = sum, seed = s_;
}
void checkb(std::string s_) {
  for (int i = 0; i < 4; ++ i) {
    for (int j = 0; j < i; ++ j) {
      if (s_[i] == s_[j]) return ;
    }
  }

  int sum = 0;
  for (int i = 0; i < m; ++ i) {
    int s1 = 0, s2 = 0;
    std::string t1 = s_.substr(2 * (i % 2 == 1), 2);
    std::string t2 = t1.substr(1, 1) + t1.substr(0, 1);
    for (int j = 0; j < n; j += 2) {
      for (int k = j, l = 0; k <= std::min(j + 1, n - 1); ++ k, ++ l) {
        if (a[k][i] != t1[l]) ++ s1;
        if (a[k][i] != t2[l]) ++ s2;
      }
    }
    sum += std::min(s1, s2);
  }
  if (sum < minsum) type = 1, minsum = sum, seed = s_;
}
void getansa() {
  for (int i = 0; i < n; ++ i) {
    int s1 = 0, s2 = 0;
    std::string t1 = seed.substr(2 * (i % 2 == 1), 2);
    std::string t2 = t1.substr(1, 1) + t1.substr(0, 1);
    for (int j = 0; j < m; j += 2) {
      for (int k = j, l = 0; k <= std::min(j + 1, m - 1); ++ k, ++ l) {
        if (a[i][k] != t1[l]) ++ s1;
        if (a[i][k] != t2[l]) ++ s2;
      }
    }
    ans.push_back("");
    if (s1 <= s2) {
      while ((int) ans[i].length() + 2 <= m) ans[i] += t1;
      if ((int) ans[i].length() < m) ans[i].push_back(t1[0]);
    } else {
      while ((int) ans[i].length() + 2 <= m) ans[i] += t2;
      if ((int) ans[i].length() < m) ans[i].push_back(t2[0]);
    }
  }
}
void getansb() {
  for (int i = 0; i < n; ++ i) ans.push_back("");

  for (int i = 0; i < m; ++ i) {
    int s1 = 0, s2 = 0;
    std::string t1 = seed.substr(2 * (i % 2 == 1), 2);
    std::string t2 = t1.substr(1, 1) + t1.substr(0, 1);
    for (int j = 0; j < n; j += 2) {
      for (int k = j, l = 0; k <= std::min(j + 1, n - 1); ++ k, ++ l) {
        if (a[k][i] != t1[l]) ++ s1;
        if (a[k][i] != t2[l]) ++ s2;
      }
    }
    int len = 0;
    if (s1 <= s2) {
      for (; len + 2 <= n; len += 2) {
        ans[len].push_back(t1[0]);
        ans[len + 1].push_back(t1[1]);
      }
      if (len < n) ans[len].push_back(t1[0]); 
    } else {
      for (; len + 2 <= n; len += 2) {
        ans[len].push_back(t2[0]);
        ans[len + 1].push_back(t2[1]);
      }
      if (len < n) ans[len].push_back(t2[0]);
    }
  }
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  Init();
  for (int i = 0; i < 4; ++ i) {
    for (int j = 0; j < 4; ++ j) {
      for (int k = 0; k < 4; ++ k) {
        for (int l = 0; l < 4; ++ l) {
          std::string s;
          s.push_back(ch[i]), s.push_back(ch[j]);
          s.push_back(ch[k]), s.push_back(ch[l]);
          checka(s), checkb(s);
        }
      }
    }
  }
  type ? getansb() : getansa();
  for (auto s: ans) std::cout << s << "\n";
  return 0;
}

F

线段树,博弈,DP

首先考虑二分答案,检查在至少吃 \(\operatorname{lim}\) 个饼☆下能否到达一个必胜节点即可。节点 \(u\) 是获胜节点当且仅当满:

  • \(T - 2\times \operatorname{dis}(1, u)\) 时间内,按照所需时间升序吃路径 \(1\rightarrow u\) 上的饼干可以吃到至少 \(\operatorname{lim}\) 个。考虑对路径 \(1\rightarrow u\) 上所有饼干维护权值线段树,下标为吃完饼干所需时间,维护区间饼干数量与吃完区间所有饼干所需时间,线段树上二分即可。
  • 不满足条件一,且 \(u\not= 1\),则子节点中应至少有两个必胜节点,使得在删掉一个子节点后仍可获胜。
  • 不满足条件一二,且 \(u=1\),则子节点中应至少有一个必胜节点。

于是在 DFS 的过程中对路径 \(1\rightarrow u\) 上所有饼干维护权值线段树,回溯时更新该节点是否为必胜节点即可。

若节点 1 为必胜节点则返回 True

总时间复杂度 \(O(n\log^2 \left(\max t\right))\)

//线段树,博弈,DP
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 1e5 + 10;
//=============================================================
int n;
int edgenum, head[kN], v[kN], w[kN], ne[kN];
int cnt[kN], maxt, tim[kN];
bool yes[kN];
LL T, sumc, ans;
//=============================================================
namespace Seg {
  #define ls (now_<<1)
  #define rs (now_<<1|1)
  #define mid ((L_+R_)>>1)
  const int kNode = 4e6 + 10;
  LL sum[kNode], cnt[kNode];
  void Pushup(int now_) {
    sum[now_] = sum[ls] + sum[rs];
    cnt[now_] = cnt[ls] + cnt[rs];
  }
  void Insert(int now_, int L_, int R_, int pos_, LL sum_, LL cnt_) {
    if (L_ == R_) {
      sum[now_] += sum_;
      cnt[now_] += cnt_;
      return ;
    }
    if (pos_ <= mid) Insert(ls, L_, mid, pos_, sum_, cnt_);
    else Insert(rs, mid + 1, R_, pos_, sum_, cnt_);
    Pushup(now_);
  }
  int Query(int now_, int L_, int R_, LL lim_) {
    if (L_ == R_) {
      return std::min(cnt[now_], lim_ / L_);
    }
    LL sl = sum[ls], cl = cnt[ls];
    if (lim_ <= sl) return Query(ls, L_, mid, lim_);
    return cl + Query(rs, mid + 1, R_, lim_ - sl);
  }
  #undef ls
  #undef rs
  #undef mid
}
void Add(int u_, int v_, int w_) {
  v[++ edgenum] = v_;
  w[edgenum] = w_;
  ne[edgenum] = head[u_];
  head[u_] = edgenum;
}
void Init() {
  std::cin >> n >> T;
  for (int i = 1; i <= n; ++ i) {
    std::cin >> cnt[i];
    sumc += cnt[i];
  }
  for (int i = 1; i <= n; ++ i) {
    std::cin >> tim[i];
    maxt = std::max(maxt, tim[i]);
  }
  for (int i = 2; i <= n; ++ i) {
    int u_, w_; std::cin >> u_ >> w_;
    Add(u_, i, w_);
  }
}
void Dfs(int u_, LL dis_, LL lim_) {
  if (dis_ > T) return ;
  Seg::Insert(1, 1, maxt, tim[u_], 1ll * tim[u_] * cnt[u_], cnt[u_]);

  int yesc = 0;
  for (int i = head[u_]; i; i = ne[i]) {
    Dfs(v[i], dis_ + w[i], lim_);
    yesc += yes[v[i]];
  }
  yes[u_] = (Seg::Query(1, 1, maxt, T - 2ll * dis_) >= lim_) || (yesc >= 2);
  if (u_ == 1) yes[u_] = yes[u_] || (yesc >= 1);
  
  Seg::Insert(1, 1, maxt, tim[u_], -1ll * tim[u_] * cnt[u_], -cnt[u_]);
}
bool check(LL lim_) {
  memset(yes, 0, (n + 1) * sizeof (bool));
  Dfs(1, 0, lim_);
  return yes[1];
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  Init();

  for (LL l = 0, r = sumc; l <= r; ) {
    LL mid = ((l + r) >> 1ll);
    if (check(mid)) {
      ans = mid;
      l = mid + 1;
    } else {
      r = mid - 1;
    }
  }
  std::cout << ans << "\n";
  return 0;
}

写在最后

学到了什么:

  • E:我草我不想写码农提
  • F:必胜点的 DP
posted @ 2024-03-05 15:16  Luckyblock  阅读(6)  评论(0编辑  收藏  举报