接着剤の精進日記

競プロでの精進や研究に関係したことを書いていきます。

Codeforces Round #674 (Div. 3)

codeforces.com

A. Floor Number

n \leq 2なら答えは1
そうでないなら\lceil \frac{n - 2}{x} \rceil + 1
提出コード

void solve(){
    int n, x;
    cin >> n >> x;
    if(n <= 2) cout << 1 << endl;
    else cout << ((n - 2) + x - 1) / x + 1 << endl;
}

B. Symmetric Matrix

まずmが奇数の時答えは"NO"
それ以外の時a_{0,1} = a_{1,0}のタイルが1つでもあれば条件を満たせる
提出コード

void solve(){
    int n, m;
    cin >> n >> m;
    bool ok = false;
    REP(i,n){
        int a, b, c, d;
        cin >> a >> b >> c >> d;
        if(b == c) ok = true;
    }
    if(ok and m % 2 == 0) cout << "YES" << endl;
    else cout << "NO" << endl;
}

C. Increase and Copy

操作を考えると最初の要素をある数字まで増やしてその後コピーするのが効率がいい
これを愚直に調べると最大ケースでも大体\sqrt{N}くらいで答えが最小になることがわかる
そのため、1 \leq i \leq \sqrt{N}の範囲でi個まで要素をコピーする時の最小の値を全探索すればいい
提出コード

void solve(){
    int n;
    cin >> n;
    ll ans = n-1;

    for(int i=1;i*i<=n;i++){
        ll cnt = (n + i - 1) / i;
        ll tmp = cnt - 1 + i - 1;
        chmin(ans, tmp);
    }
    cout << ans << endl;
}

D. Non-zero Segments

基本的にはZero-Sum Rangesのような感じ
mapなどで累積和を保存していってすでに出た値があればその区間の総和は0になる
この問題の場合、その区間があったら今見ている値の一個前にクソデカ定数を適当に入れればこれ以前の区間で総和が0になるものはなくなる
そのため、総和が0になる区間が見つかれば、答えを加算しmapを初期化する
提出コード

void solve(){
    int n;
    cin >> n;
    vector<ll> a(n);
    REP(i,n) cin >> a[i];
    map<ll, int> mp;
    ll sum = 0;
    mp[sum]++;
    ll ans = 0;
    REP(i,n){
        sum += a[i];
        if(mp[sum] > 0){
            ans++;
            mp = map<ll, int>();
            sum = a[i];
            mp[0]++;
        }
        mp[sum]++;
    }
    cout << ans << endl;
}

E. Rock, Paper, Scissors

まず勝てる最大を考える
自分がグーで相手がチョキのとき勝てる最大は双方の小さい方となる
そのため、同様に自分がチョキとパーの場合の最大を求めてその和が答え
次に、最小の場合を考える
自分がグーを出すときできるだけあいこと負けで消費をする必要がある
自分のグーから相手のグーとパーを引いて値が正ならその残りは相手のチョキと戦って勝つことになる
aを自分の手、bを相手の手、グーチョキパーを0,1,2とする
そうするとmax(0, a_i - b_i - b_{(i-1+3)\%3}) (0 \leq i \leq 2)の最小値が答えとなる
フロー流してもいいらしい
提出コード

void solve(){
    int n;
    cin >> n;
    vector<int> a(3), b(3);
    REP(i,3) cin >> a[i];
    REP(i,3) cin >> b[i];
    int mi = 0, ma = 0;
    ma = min(a[0], b[1]) + min(a[1], b[2]) + min(a[2], b[0]);
    REP(i,3){
        chmax(mi, max(0, a[i] - b[i] - b[(i-1+3)%3]));
    }
    cout << mi << " " << ma << endl;
}

提出コード(フロー)

void solve(){
    int n;
    cin >> n;
    vector<int> a(3), b(3);
    REP(i,3) cin >> a[i];
    REP(i,3) cin >> b[i];
    int s = 6, t = 7;
    Dinic<ll> g(8);
    REP(i,3) g.add_edge(s, i, a[i]);
    REP(i,3) g.add_edge(i+3, t, b[i]);
    REP(i,3) REP(j,3) if((i + 1) % 3 != j){
        g.add_edge(i, 3+j, INF);
    }
    ll ma = min(a[0], b[1]) + min(a[1], b[2]) + min(a[2], b[0]);
    ll mi = n - g.flow(s,t);
    cout << mi << " " << ma << endl;
}

F. Number of Subsequences

AtCoderで既出
"a"、"ab"、"abc"が幾つ作れるかをdpで数えていけばいい
atcoder.jp

提出コード

void solve(){
    int n;
    string s;
    cin >> n >> s;
    vector<vector<mint>> dp(n+10, vector<mint>(5));
    dp[0][0] = 1;
    REP(i,n){
        REP(j,5){
            if(s[i] == '?') dp[i+1][j] += dp[i][j] * 3;
            else dp[i+1][j] += dp[i][j];
        }
        if(s[i] == 'a' or s[i] == '?') dp[i+1][1] += dp[i][0];
        if(s[i] == 'b' or s[i] == '?') dp[i+1][2] += dp[i][1];
        if(s[i] == 'c' or s[i] == '?') dp[i+1][3] += dp[i][2];
    }
    cout << dp[n][3] << endl;
}