AtCoder Beginner Contest 338 F,G 問題メモ

F - Negative Traveling Salesman

問題

  • $N$ 頂点 $M$ 辺の重み付き単純有向グラフがある
  • 辺は負の重みをとることもあるが、負閉路は存在しない
  • 全ての頂点を1度以上通るようなウォークがあるか判定し、ある場合は通る辺の重みの総和の最小値を求めよ
    • 同じ辺を2度以上通ってもよいが、コストは通るたびに加算される
  • $2 \le N \le 20$

解法

Floyd-Warshall は、負辺があっても負閉路がなければ全頂点間同士の最短距離を求められる。

また、巡回セールスマンも通った頂点集合が小さい方から探索を進めれば、コストに負が出てきても大丈夫。
(Dijkstra的に、コストが小さい方から探索してしまうと、負辺が出てくる場合は上手くいかない)

Floyd-Warshall で求めた全頂点間の最短距離を使って、巡回セールスマンをおこなえば、答えとなる。

証明

実装上の留意点として、負辺を含む場合、DP配列の初期値として十分大きい数 $INF=10^{18}$ などで“不可能な状態”を表していると、 負辺を通って「INFよりちょっと少ない数」で更新されてしまう可能性があり、「最終的な結果=INFなら不可能」と言えなくなる。

  • いずれかの対策を取る必要がある。
    • 答えの取り得る範囲を $-X~X$ として、$2X$ 以上の数で初期化して、$X$ 以上なら「不可能」と判定する
    • INFと定めた値からは更新しないようにする

巡回セールスマンの時の $DP[S,i]$ の $S$ って、通常の場合は「既に訪れた頂点」と意味づけることが多いので、 その意味合いのまま、今回の問題で元のウォークの状態を表現しようとすると、 「Froyd-Warshallにおける最短距離を実現するパス」の情報まで必要になってしまい、実装が大変になった。

途中の通過点を無視した実装でも、ちゃんといけるんだね。

Python3

G - evall

問題

  • 以下を全て満たす文字列を「計算式として評価できる文字列」とする
    • '0'以外の数字と、'+','*'のみからなる
    • 先頭と末尾の文字が数字
    • 数字でない文字は2個以上連続しない
  • 計算式として評価できる文字列 $S$ が与えられる
  • $S$ の全ての(連続した)部分文字列のうち、計算式として評価できる文字列であるものを、計算式として評価した結果の総和を$\mod{998244353}$ で求めよ
  • $1 \le |S| \le 10^6$

解法

まず、かけ算が優先されるので、そちらを先に評価した方がやりやすそう。'+'で区切ってしまう。

123*456*789+12345*6789+12*34*56*78*9
↓
[123*456*789, 12345*6789, 12*34*56*78*9]

区切られた1つ1つを「かけ算式」と呼ぶことにする。

各かけ算式について、加算される回数を求める。

①
123*456*789 ┐かけ算式内に左端があり、右端はこれより後のかけ算式内にある場合、
 23*456*789 │こいつらは、それぞれ、これより後に出てくるかけ算式の、
  3*456*789 │数字の個数分だけ加算される
    456*789 │
     56*789 │
      6*789 │
        789 │
         89 │
          9 ┘
②
123*456*789 ┐かけ算式内に右端があり、左端はこれより前のかけ算式内にある場合、
123*456*78  │こいつらは、それぞれ、これより前に出てくるかけ算式の、
123*456*7   │数字の個数分だけ加算される
  :         │
123         │
12          │
1           ┘

③
123*456*789   かけ算式を覆うように区間が設定される場合、全体が、
              (これより前に出てくる数字の個数)×(後に出てくる数字の個数)
              分だけ加算される

④かけ算式内に左端も右端もある場合(後述)

①②③は、各かけ算式のprefix,suffix(上記に列挙したような式)の総和と、数字の個数を数えておけばよい。
前からと後ろから調べれば、それぞれ $O(|S|)$ で作れる。

④は、'*'で区切って数字のみの式(数字式と呼ぶことにする)にして、1文字ずつ評価すれば、

[123, 456, 789] に分割した内の、789 の '8' を評価中として、

(A)それより前の数字式のsuffixの和
123*456 ┐
 23*456 │
   :    │
     56 │
      6 ┘

(B)現在の数字式の'8'以前の部分
78

(C)現在の数字式の'8'以前の部分のsuffixの和
78 ┐
 8 ┘

④に分類される式のうち、'8'を右端としたものの総和は、$A \times B + C$ で求められる。

数字式内では、B,Cを以下で更新していける。(数字 $x$ で更新するとする)

  • $B←10B+x$
  • $C←10C+x \times (xがその数字式の何番目の数字か)$

数字式の終端まで来たら、$A←A \times B + C$ で更新し、$B,C←0$ として次の数字式に移る。

これで全ての場合を $O(|S|)$ で求められる。

Python3

programming_algorithm/contest_history/atcoder/2024/0127_abc338.txt · 最終更新: 2024/01/31 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0