うーん
— ボンド@競プロ (@bond_cmprog) December 19, 2020
Bondo416さんのパナソニックプログラミングコンテスト(AtCoder Beginner Contest 186)での成績:1171位
パフォーマンス:1348相当
レーティング:1495→1481 (-14) :(#AtCoder #パナソニックプログラミングコンテスト(ABC186) https://t.co/yIAfE6aq4D
A - Brick
$ \lceil \frac{N}{W} \rceil $ を出力
提出コード
void solve(){ int N, W; cin >> N >> W; cout << N / W << endl; }
B - Blocks on Grid
一番小さい値に合わせればいいのでその差分の和が答え
提出コード
void solve(){ int H, W; cin >> H >> W; vector A(H, vector(W, 0)); int mi = INF; REP(i,H) REP(j,W){ cin >> A[i][j]; chmin(mi, A[i][j]); } int ans = 0; REP(i,H) REP(j,W) ans += A[i][j] - mi; cout << ans << endl; }
C - Unlucky 7
正の整数を10進法と8進法に変換するのは愚直にやっても間に合うので全探索すればいい
提出コード
void solve(){ int N; cin >> N; int ans = 0; for(int i=1;i<=N;i++){ int tmp = i; bool ok = false; while(tmp > 0){ int num = tmp % 10; if(num == 7) ok = true; tmp /= 10; } tmp = i; while(tmp > 0){ int num = tmp % 8; if(num == 7) ok = true; tmp /= 8; } if(ok) ans++; } cout << N - ans << endl; }
D - Sum of difference
絶対値が扱いにくいので降順ソートをすると$ A_i - A_j \geq 0 $となって扱いやすい
$ A_i $を固定すると
$ A_i - A_{i + 1} + A_i - A_{i+2} + ... + A_i - A_{N - 1} = A_i * (N - i - 1) - \sum _{j = i + 1} ^{N - 1} A_j $
となるので、予め累積和を求めておけばいい
提出コード
void solve(){ int N; cin >> N; vector<ll> A(N); REP(i,N) cin >> A[i]; sort(A.rbegin(), A.rend()); vector<ll> cum(N+1); for(int i=N-1;i>=0;i--) cum[i] = cum[i+1] + A[i]; ll ans = 0; REP(i,N){ ans += A[i] * (N - i -1) - cum[i+1]; } cout << ans << endl; }
E - Throne
何回移動したかを$ x $とすると$ S + Kx \equiv 0 \pmod N $を満たす$ x $を求められればいい
これは$ K $の逆元が存在すれば求めることが出来る
まず、$ g = gcd(N, S, K) $とし、$N = N / g, S = S / g, K = K / g $と置き換える
$gcd(N, K) \neq 1 $のとき、$ K $の逆元が存在しないので-1
それ以外の場合逆元を求めれば良く、ACLのinv_mod
を使えばこれを求められる
また、$ y = Kx $とし、$ S + Kx \equiv 0 \pmod N $を
- $ y \equiv 0 \pmod K $
- $ y \equiv -S \pmod N $
とすれば、中国剰余定理で解が存在するかどうかと、存在すればその解を求めることが出来る
提出コード
void solve(){ ll N, S, K; cin >> N >> S >> K; ll g = gcd(gcd(N, S), K); N /= g, S /= g, K /= g; if(gcd(N, K) != 1){ cout << -1 << endl; return; } cout << (N - S + N) % N * inv_mod(K, N) % N << endl; }
void solve(){ ll N, S, K; cin >> N >> S >> K; auto [y, z] = crt({0, N-S}, {K, N}); if(y == 0) cout << -1 << endl; else cout << y / K << endl; }
F - Rook on Grid
移動方法を考えると、最初に右に移動し、次に下に移動する、もしくは最初に下に移動し、次に右に移動するの2パターン考えられる
右・下の移動を考えると、各列において最初にぶつかる障害物までの位置を求めることで移動可能なマスを数えられ、下・右の場合も同様に求められる
$ w_i $を$ i $列目に一番上にある障害物の位置、$ h_i $を$ i $行目の一番左にある障害物の位置とすると
それぞれの移動可能なマスは$ \sum _{i = 0} ^{h_0} w_i + \sum _{i = 0} ^{w_0} h_i $となる
ただし、重複して数えているマスがあるので重複マスを引いて上げる必要がある
上から順に各行を見ていき、各マスがすでに障害物が出現したかどうかをBITなどで管理し、その総和を引いていけばいい
提出コード
void solve(){ int H, W, M; cin >> H >> W >> M; vector<int> h(H, W), w(W, H); REP(i,M){ int x, y; cin >> x >> y; --x, --y; chmin(w[y], x); chmin(h[x], y); } ll ans = 0; REP(i,w[0]) ans += h[i]; REP(i,h[0]) ans += w[i]; BIT<int> bit(W); REP(i,h[0]) bit.add(i,1); vector<vector<int>> obj(H+1); REP(i,h[0]) obj[w[i]].emplace_back(i); REP(i,w[0]){ for(auto j : obj[i]) bit.add(j, -1); ans -= bit.sum(0, h[i]); } cout << ans << endl; }