AtCoder Beginner Contest 257

咕咕咕咕咕。

F - Teleporter Setting

题意

给一个有n个节点m条边的无向图,边集中有一部分边只确定了一端,记这部分边的集合为S

对于i=1,2,,n,问当S中的边的不确定的那一端均为i时,从点1到点n的最短路长度。

其中,2n3×105,1m3×105

思路

新增一个虚拟节点n+1,将S中的边的不确定的那一段都看成是n+1

在询问i时,可以看成是在in+1之间连一条长度为0的边。

此时的最短路,要么不包含S中的边,要么是1n+1in1in+1n

然后就是dijkstra板子了。

AC代码
// Problem: F - Teleporter Setting
// Contest: AtCoder - NS Solutions Corporation Programming Contest 2022(AtCoder Beginner Contest
// 257)
// URL: https://atcoder.jp/contests/abc257/tasks/abc257_f
// Memory Limit: 1024 MB Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

#define CPPIO std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
#define freep(p) p ? delete p, p = nullptr, void(1) : void(0)

#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#endif

using i64 = int64_t;
using u64 = uint64_t;

void solve_case(int Case);

int main(int argc, char* argv[]) {
  CPPIO;
  int T = 1;
  // std::cin >> T;
  for (int t = 1; t <= T; ++t) {
    solve_case(t);
  }
  return 0;
}

template <typename DistanceType>
struct Edge {
  int v_;
  DistanceType w_;
  Edge() {}
  Edge(int v, DistanceType w) : v_(v), w_(w) {}
};

template <typename DistanceType>
std::vector<DistanceType> Dijkstra(const std::vector<std::vector<Edge<DistanceType>>>& g, int s) {
  using Node = std::pair<DistanceType, int>;
  const DistanceType INF = std::numeric_limits<DistanceType>::max();
  const int n = g.size();

  std::vector<DistanceType> dis(n, INF);
  std::vector<bool> vis(n, false);

  std::priority_queue<Node, std::vector<Node>, std::greater<Node>> q;
  dis[s] = 0;
  q.push(Node(dis[s], s));
  while (!q.empty()) {
    auto [c, u] = q.top();
    q.pop();

    if (vis[u])
      continue;
    vis[u] = true;

    for (auto [v, w] : g[u]) {
      if (dis[v] > c + w) {
        dis[v] = c + w;
        q.push(Node(dis[v], v));
      }
    }
  }
  return dis;
}

void solve_case(int Case) {
  int n, m;
  std::cin >> n >> m;

  std::vector<std::vector<Edge<int>>> g(n + 1);
  for (int i = 1; i <= m; ++i) {
    int u, v;
    std::cin >> u >> v;
    if (u == 0) {
      u = n + 1;
    }
    --u, --v;
    g[u].push_back(Edge(v, 1));
    g[v].push_back(Edge(u, 1));
  }

  std::vector<int> ds = Dijkstra(g, 0);
  std::vector<int> dt = Dijkstra(g, n - 1);
  const int INF = std::numeric_limits<int>::max();

  for (int i = 0; i < n; ++i) {
    int mi = ds[n - 1];
    if (ds[n] != INF && dt[i] != INF)
      mi = std::min(mi, ds[n] + dt[i]);
    if (ds[i] != INF && dt[n] != INF)
      mi = std::min(mi, ds[i] + dt[n]);

    if (mi == INF)
      mi = -1;
    std::cout << mi << " ";
  }
}


G - Prefix Concatenation

题意

给两个字符串st,问最少用多个s的前缀能拼出t

其中1|s|,|t|5×105

思路

dpi表示最少用多少个s的前缀能够拼出t长度为i的前缀。

然后猜了个结论,就是把令z=s+#+t,然后跑kmp求出z每个位置的最长border长度记为数组p,则dpi只从dpipi处转移即可。

AC代码
// Problem: G - Prefix Concatenation
// Contest: AtCoder - NS Solutions Corporation Programming Contest 2022(AtCoder Beginner Contest
// 257)
// URL: https://atcoder.jp/contests/abc257/tasks/abc257_g
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

#define CPPIO std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
#define freep(p) p ? delete p, p = nullptr, void(1) : void(0)

#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#endif

using i64 = int64_t;
using u64 = uint64_t;

void solve_case(int Case);

int main(int argc, char* argv[]) {
  CPPIO;
  int T = 1;
  // std::cin >> T;
  for (int t = 1; t <= T; ++t) {
    solve_case(t);
  }
  return 0;
}

std::vector<int> kmp(const std::string& s) {
  int n = s.size();
  std::vector<int> p(n);

  p[0] = 0;
  for (int i = 1; i < n; ++i) {
    int j = p[i - 1];
    while (j > 0 && s[j] != s[i])
      j = p[j - 1];
    if (s[i] == s[j])
      ++j;
    p[i] = j;
  }

  return p;
}

void solve_case(int Case) {
  std::string s;
  std::string t;
  std::cin >> s;
  std::cin >> t;
  int n = s.size(), m = t.size();

  s = s + "#" + t;
  auto p = kmp(s);

  std::vector<int> dp(m + 1, 0x3f3f3f3f);
  dp[0] = 0;
  for (int i = 1; i <= m; ++i) {
    int l = p[n + i];
    dp[i] = std::min(dp[i], dp[i - l] + 1);
  }
  if (dp[m] == 0x3f3f3f3f)
    dp[m] = -1;
  std::cout << dp[m] << "\n";
}

Ex - Dice Sum 2

题意

给定 n 个六面骰子,第 i 个骰子的第 j 面上面的数字为 ai,j,第 i 个骰子值 ci 块钱。

要从这 n 个骰子中选择 k 个使得 A2B 取值的期望最大,其中 A 为所选骰子每个骰子摇一次,显示数字之和的平方, B 为所选骰子的价值之和。

其中 1n1000

思路

问题转化

假设所选的骰子集合为D={i1,,ik},根据期望的定义,有

ED=16kiD1j1,,jk6(ai1,j1++aik,jk)2iDci=16k(iD1j66k1ai,j22i,jD,ij1l,h66k2ai,haj,l)iDci=16iD1j6ai,j2118i,jD,ij1l,h6ai,haj,liDci

Si=j=16ai,j,Qi=j=16ai,j2,则有

ED=16iDQi+136(iDSi)2136iDSi2iDci

Ai=Si,Bi=6QiSi236ci,XD=iDAi,YD=iDBi,则有

ED=XD2+YD

把一个选择方案 D 看成二位平面上的一个点 (XD,YD) ,现在问题转化为给定点集,要求最大化x2+y

问题求解

对于最优的选择方案 D ,一定存在某个实数 z 使得iD,jD,zAi+BizAj+Bj

证明

考虑在某条线段上恒速运动的点(x,y)x2+y的图像是关于 x 的一个开口向上的抛物线,由此其极值点必定在两个端点之一。拓展到多个点时,则有极值点必定位于点集的凸包的顶点。

对于每一个顶点,都有某一个 z ,使得 zx+y 最大化。

对于某一个 z,也可以找到对应的一个顶点使得 zx+y 最大化,这个顶点对应的选择方案为选择 zAi+Bik 大的点。

求解

考虑从小到大搜索 z 的值,复杂度爆炸。

上述过程可以分成多段结果相同的段。特别地,如果把 (Ai,Bi)zAi+Bi 排序,只有在 zzAi+Bi=zAj+Bj 时,或者说z=BiBjAjAi时,结果会发生改变,改变为 (Ai,Bi)(Aj,Bj) 交换位置,原本位于 i,j 中间的点保持不变。

易得只有O(n2)个使结果发生变化的 z ,每对点也至多交换一次。

不妨先令 z=+,然后再不断降低 z 的取值,从而搜索所有可能的交换,单次交换至多交换两个点。

时间复杂度为 O(n2logn),瓶颈在于快排。

补充

求解部分kmjp's blog这个博客讲的比官方题解具体。

AC代码
// Problem: Ex - Dice Sum 2
// Contest: AtCoder - NS Solutions Corporation Programming Contest 2022(AtCoder Beginner Contest
// 257) URL: https://atcoder.jp/contests/abc257/tasks/abc257_h Memory Limit: 1024 MB Time Limit:
// 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

#define CPPIO std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);

#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#define ASSERT(x) ;
#define serialize() ""
#endif

using i64 = int64_t;
using u64 = uint64_t;

void Initialize();
void SolveCase(int Case);

int main(int argc, char* argv[]) {
  CPPIO;
  int T = 1;
  // std::cin >> T;
  for (int t = 1; t <= T; ++t) {
    SolveCase(t);
  }
  return 0;
}

void Initialize() {}

template <typename ValueType, ValueType mod_, typename SupperType>
class Modular {
  static ValueType normalize(ValueType value) {
    if (value >= 0 && value < mod_)
      return value;
    value %= mod_;
    if (value < 0)
      value += mod_;
    return value;
  }

  static ValueType power(ValueType value, size_t exponent) {
    ValueType result = 1;
    ValueType base = value;
    while (exponent) {
      if (exponent & 1)
        result = SupperType(result) * base % mod_;
      base = SupperType(base) * base % mod_;
      exponent >>= 1;
    }
    return result;
  }

 public:
  Modular(ValueType value = 0) : value_(normalize(value)) {}

  Modular(SupperType value) : value_(normalize(value % mod_)) {}

  ValueType value() const { return value_; }

  Modular inv() const { return Modular(power(value_, mod_ - 2)); }

  Modular power(size_t exponent) const { return Modular(power(value_, exponent)); }

  friend Modular operator+(const Modular& lhs, const Modular& rhs) {
    ValueType result = lhs.value() + rhs.value() >= mod_ ? lhs.value() + rhs.value() - mod_
                                                         : lhs.value() + rhs.value();
    return Modular(result);
  }

  friend Modular operator-(const Modular& lhs, const Modular& rhs) {
    ValueType result = lhs.value() - rhs.value() < 0 ? lhs.value() - rhs.value() + mod_
                                                     : lhs.value() - rhs.value();
    return Modular(result);
  }

  friend Modular operator-(const Modular& lhs) {
    ValueType result = normalize(-lhs.value() + mod_);
    return result;
  }

  friend Modular operator*(const Modular& lhs, const Modular& rhs) {
    ValueType result = SupperType(1) * lhs.value() * rhs.value() % mod_;
    return Modular(result);
  }

  friend Modular operator/(const Modular& lhs, const Modular& rhs) {
    ValueType result = SupperType(1) * lhs.value() * rhs.inv().value() % mod_;
    return Modular(result);
  }

  std::string to_string() const { return std::string(value_); }

 private:
  ValueType value_;
};

// using Mint = Modular<int, 1'000'000'007, int64_t>;
using Mint = Modular<int, 998'244'353, int64_t>;

class Binom {
 private:
  std::vector<Mint> f, g;

 public:
  Binom(int n) {
    f.resize(n + 1);
    g.resize(n + 1);

    f[0] = Mint(1);
    for (int i = 1; i <= n; ++i)
      f[i] = f[i - 1] * Mint(i);
    g[n] = f[n].inv();
    for (int i = n - 1; i >= 0; --i)
      g[i] = g[i + 1] * Mint(i + 1);
  }
  Mint operator()(int n, int m) {
    if (n < 0 || m < 0 || m > n)
      return Mint(0);
    return f[n] * g[m] * g[n - m];
  }
};

void SolveCase(int Case) {
  int n, k;
  std::cin >> n >> k;

  std::vector<i64> a(n), b(n), c(n);
  for (int i = 0; i < n; ++i)
    std::cin >> c[i];

  for (int i = 0; i < n; ++i) {
    i64 s = 0, s2 = 0;
    for (int j = 0; j < 6; ++j) {
      i64 x;
      std::cin >> x;

      s += x;
      s2 += x * x;
    }

    a[i] = s;
    b[i] = 6 * s2 - s * s - 36 * c[i];
  }

  // let z = +\inf first, and then decrease z gradually.
  std::vector<std::pair<i64, i64>> p(n);
  for (int i = 0; i < n; ++i)
    p[i] = {a[i], b[i]};
  std::sort(p.begin(), p.end(), std::greater<>());

  std::vector<int> selected(n, false);
  i64 X = 0, Y = 0;
  for (int i = 0; i < k; ++i) {
    X += p[i].first;
    Y += p[i].second;
    selected[i] = true;
  }
  i64 ans = X * X + Y;

  // decrease z gradually.
  std::vector<std::array<i64, 4>> swaps;
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < n; ++j) {
      auto [xi, yi] = p[i];
      auto [xj, yj] = p[j];

      // pi can replace pj only if xi < xj and yi > yj since we are maximizing cx + y.
      if (xi < xj && yi > yj) {
        swaps.push_back({xi - xj, yi - yj, i, j});
      }
    }
  }
  std::sort(swaps.begin(), swaps.end(),
            [&](const std::array<i64, 4>& lhs, const std::array<i64, 4>& rhs) -> bool {
              auto [dx1, dy1, _1, _2] = lhs;
              auto [dx2, dy2, _3, _4] = rhs;

              return dx1 * dy2 > dx2 * dy1;
            });
  for (auto [_1, _2, i, j] : swaps) {
    if (!selected[i] && selected[j]) {
      X += p[i].first - p[j].first;
      Y += p[i].second - p[j].second;
      ans = std::max(ans, X * X + Y);

      selected[i] = true;
      selected[j] = false;
    }
  }
  std::cout << (Mint(ans) / Mint(36)).value() << "\n";
}

posted @   _Backl1ght  阅读(167)  评论(4编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示