96:30で7完、初の全完です!! 196位で2324パフォでした。カンストパフォは逃してしまいましたがうれしい!
うれしかったので久しぶりに参加記を書きます。
問題: Modulo MST
頂点 辺の重み付き無向グラフと正整数 が与えられます。 このグラフの全域木 に対して、コストを使った辺の重みの総和を で割ったあまりとします。 全域木コストの最小値はいくつですか?
あんまりよくわかりませんでした。 今選んでいる頂点の集合をbitで管理し、選んでいない頂点への辺を採用していくDPを書いて無理やり通しました。
コンテスト終了後選ぶ辺を 個列挙する方法を知りました、なるほどです。冷静に考えると間に合いますね。
また、個の と 個の を next_permutation
に突っ込むテクも知りました。賢いです。
問題: Good Set Query
重み付きUnionFindを知っていますか?と問題文に書いてあるので窃盗しました。いずれライブラリにしたいです。
問題: Cut and Reorder
長さ の数列 があり、次の操作を好きな回数好きな順序で行うことで を に一致させたいです。
を に一致させるのに必要なコストの合計の最小値はいくつですか?
最初フローっぽい!と言いながら虚無の考察をしてしまいました。
まず、操作2は最後にそれぞれやればよいです。操作1について考えるのですが、この操作をするのは高々 回でよいという大胆予想をします。 この大胆予想により、 を移動させ、移動後の集合が となるような移動にかかるコストの最小値 というDPをしたくなります。遷移は配るDPで、いくつかをまとめて移動できるなら移動して更新のようにすればよいです。
ここで、 の情報は の立っているbitの個数と同じなので、この情報は持たなくてよいです。 コンテスト中は特に意識せずこれをしたのですが、これをしないとMLEになるらしいです。
あとは操作1にかかるコストについてなのですが、これはDPの初期値を とし、まとめて 個の要素を移動したときに コストを すれば良いです。これを急いで実装し、ACすることができました!
#include <bits/stdc++.h>using namespace std;
using ll = long long;using vll = vector<ll>;template<class T> bool chmin(T &a, const T &b) {if(b<a){ a=b; return 1;} return 0;}#define INF (1LL << 60)#define rep(i, n) for (ll i = 0; i < (n); i++)
int main() {
ll N, C; cin >> N >> C; vll A(N), B(N); for (auto &a : A) cin >> a; for (auto &b : B) cin >> b;
vll dp(1 << N, INF); dp[0] = (N - 1) * C;
rep(msk, (1 << N)) { ll cost = dp[msk]; ll i = __builtin_popcountll(msk); vll mov; for (ll w = 1; w + i <= N; w++) { mov.push_back(A[i + w - 1]); ll wmsk = (1 << w) - 1; for (ll j = 0; j + w <= N; j++) { if ((msk | (wmsk << j)) == (msk + (wmsk << j))) { ll nmsk = msk | (wmsk << j); ll ncost = cost; for (int k = 0; k < w; k++) { ncost += abs(mov[k] - B[j + k]); } ncost -= (w - 1) * C; chmin(dp[nmsk], ncost); } } } }
cout << dp[(1 << N) - 1] << endl;}
最近レートが大幅にマイナスになる回が多くて悲しかったのですが、挽回できてよかったです。 このままレートを上げて黄色になりたいです!