CF 847 J - Students Initiation
問題
問題概要
頂点辺の無向グラフが与えられる。この各辺について、どちらかに向きを付けなければならない。その時に各頂点からの出次数の最大値が最小になるような割り当て方を答えよ。
- 多重辺や自己ループはない
アイデア
各辺について、端点のどちらかに+1をする時に、頂点の持つ値の最大値を最小化するというように見ることが出来る。答えを二分探索することを考える。各頂点はまでは増やすことを許すとする。このが妥当かどうかをフローを使って解く。フローを考えるグラフは次のように構築する:
- から、へ容量1
- から、とへそれぞれ容量1
- から、へ容量
このようなグラフを構築し、最大フローを求める。その値がに一致すればOKとなる。このフローは、Dinicを使えば高速に動作する(のとき、Karzanov's theoremにより、Dinicはで動作するらしい(?)。この辺は理解しきれてない)。
復元は、残余グラフを見れば簡単にできる。
実装(C++)
続きを読むCodechef October Challenge 2017 - Shooting on the array
問題
問題概要
長さの数列が与えられる。xy平面を考えて、数列に対して番目の要素について、とを端点とする線分を引く。以下の2種類のクエリが合計で個与えられるのでそれを処理せよ:
+ i X
:? i L R
: x軸に平行な光が、個発射されると想定する。番目の光は、座標からx軸正の方向に向けて発射されるが、一度線分にぶつかると光は遮断される。このとき、光が当たっている線分の個数を答える。
2つ目のクエリについては、光はクエリごとに独立で考える → 毎回このクエリごとに光源を設置して、このクエリに答えたらその光源はなくなる。
アイデア
まず、2つ目の幾何的なクエリは分かりにくいので、幾何的な要素を除外する:
であるについて、高さの光が当たるということは、であり、について、である必要がある。このことから、光が番目の線分にあたるための線分の条件は、
- (これ以上無いとそもそも光が当たらない)
- (これがないと番目の線分に光が届かない)
なので、これを満たすようなの個数を数える、ということになる。
3番目の条件はを満たす最小のを見つけ、の部分については考えないということにすれば良いので、について考えることで、3番目の条件を除外できる。を見つけることを考えると、maxを取るsegment tree上を二分探索することでで求めることが出来る。
以下、1,2の条件のみを考える。はもう関係ないので、今segment tree上でにパラメータという3つ組のクエリに答えることを考えよう。この区間に対して、というパラメータが与えられた場合の答えをとして持っているとする。
区間の要素が1つだけならこのクエリに対する答えは自明で、以上なら1になる。
区間の要素が2つ以上ならば、2つの区間に分けて再帰的に解いていくことになる。左側、右側のそれぞれのノードに対する答えをとする。左側の子ノードの区間に対して、その区間の最大値で以下のように場合分け出来る:
- 最大値が未満の場合: 左側の子ノードの区間で光が当たる線分は自明に0本なので、右側の区間にのみ再帰を伸ばしていけばよい。
- 最大値が以上の場合: 左側の子ノードの区間で光が当たる線分は1本以上あるので、左側の区間に再帰を伸ばす。その時に、右側を考えると、パラメータに依存せず答えが一定になることがわかる(左側に以上の値があれば、そちらがよりstrictな条件になるから)。そして、それはで得られる(ではないことに注意)。
つまり、どちらの場合にせよ伸ばす再帰の方向はどちらか1つなので、この再帰はsegment tree上ででクエリに答えることが出来る。実際のクエリでは、これが、個の頂点で発生するので、で処理できる。
また、更新クエリに関しては点更新なので、事前計算で持っておくべき値について、計算し直す必要のあるノードが個あるので、で処理できる。