AtCoder Beginner Contest 127
D - Integer Cards
https://atcoder.jp/contests/abc127/tasks/abc127_d
方針
はソートしてよいので、初期状態を とします。また操作の順番も入れ替えてよいので、 とします。
値を書き換えるカードを選ぶときは、最も値が小さく、かつそのとき見ている よりも値が小さいカードを選ぶべきです。また、いま は降順にソートされているので、一度 に書き換えたカードが、後の操作で別の値 に書き換えられることはありません。よって、既に 枚のカードを書き換えたとき、次に書き換える候補となるカードは となります。
を書き換える必要が無かった場合、それ以降の は全て 以上で、しかも以降現れる は全て今見ている 以下の値ですので、操作はこの時点で打ち切ることが出来ます。また を書き換えた場合も、以降の書き換えの候補が無くなるため、操作をそこで終了してよいです。
操作を打ち切った時点での の総和を出力すれば答えになります。
ソースコード
#include<bits/stdc++.h> #define ll long long using namespace std; int main(){ int N, M; cin >> N >> M; vector<int> A(N+1,0); for(int i=1; i<=N; i++){ cin >> A[i]; } // A を昇順にソート sort(A.begin(), A.end()); vector<pair<int,int>> P(M); // P[i] = {B[i], C[i]} for(int i=0; i<M; i++){ cin >> P[i].first >> P[i].second; } // P を C の降順にソートする sort(P.begin(), P.end(), [](const pair<int,int> &a, const pair<int,int> &b){ return a.second > b.second; }); int n = 0; // 既に書き換えたカードの枚数 for(int i=0; i<M; i++){ int B = P[i].first; int C = P[i].second; // 最大で B 回カードを書き換える。 // ただし A[N] を書き換えたら打ち切る。 // また書き換え候補の A[n+1] が C 以上になっても打ち切る。 while(B>0 && n+1<=N && A[n+1]<C){ A[n+1] = C; n++; } } ll ans = 0; for(int i=1; i<=N; i++){ ans += A[i]; } cout << ans << endl; }
E - Cell Distance
https://atcoder.jp/contests/abc127/tasks/abc127_e
制約の を と勘違いし、最初は途方に暮れていました。
方針
個中、2個の駒の置き方を決めたとすると、残りの駒の置き方は 通りです。また、2個の駒の置き方のうち 座標の差の絶対値が となる駒の置き方の数を 通りとし、 を同様に定義します。すると答えは以下で表せます:
よって および を求めることに帰着しました。
(1) を求める
のときは答えに影響しないので、 とします。
座標の差が となるような 2 マスの列の選び方は、
の 通りです。
また 座標の差は行の選び方には依らないので、行の組み合わせは何でもよく、 通りです。
よって、以上合わせて となります。同様に です。
(2) を求める
を で求めます。
まず は、 1 から NM - 2 まで順番に掛けながら mod を取ることで求めることが出来ます。
次に と を で求めます。これは を求めてから、 のように掛けていけばよいです。肝心の ですが、フェルマーの小定理:
が素数のとき
が成り立つことを用いると、 となります。 乗の計算も愚直にやると当然TLEするので、繰り返し二乗法を用います。繰り返し二乗法については、AtCoder Typical Contest 002 - B を見てください。
ソースコード
以下のソースコードでは階乗やその逆数は 1 ~ まで全て求めています。
#include<bits/stdc++.h> #define ll long long using namespace std; ll mod_pow(ll n, ll p, ll mod){ // 繰り返し二乗法で n^p を mod で割った余りを計算する if(p==0) return 1; ll res = mod_pow(n*n%mod, p/2, mod); if(p%2==1) res = res * n % mod; return res; } int main(){ ll N, M, K; cin >> N >> M >> K; ll mod = 1e9+7; vector<ll> fct(N*M-1,1); // fct[n] = n! の mod for(ll i=1; i<=N*M-2; i++){ fct[i] = i * fct[i-1] % mod; } vector<ll> invfct(N*M-1,1); // invfct[n] = 1/n! の mod invfct[N*M-2] = mod_pow(fct[N*M-2], mod-2, mod) % mod; // 1/(NM-2)! を繰り返し二乗法で求める for(ll i=N*M-3; i>=1; i--){ invfct[i] = (i+1)*invfct[i+1] % mod; } // Combination(NM-2, K-2) を計算 ll W = (fct[N*M-2]*invfct[K-2] % mod)*invfct[N*M-K] % mod; ll ans = 0; // dx についてのシグマを計算 for(ll dx=1; dx<M; dx++){ ll A = (N*N*(M-dx)) % mod; ans = (ans + A * dx % mod) % mod; } // dy についてのシグマを計算 for(ll dy=1; dy<N; dy++){ ll A = (M*M*(N-dy)) % mod; ans = (ans + A * dy % mod) % mod; } // 最後に全体に W をかける cout << (ans*W)%mod << endl; }