目次

AtCoder Beginner Contest 338 F,G 問題メモ

AtCoder Beginner Contest 338

F - Negative Traveling Salesman

F - Negative Traveling Salesman

問題

解法

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

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

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

証明

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

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

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

Python3

G - evall

G - evall

問題

解法

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

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$ で更新するとする)

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

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

Python3