競プロやります。

競プロを頑張るブログです。

AtCoder Beginner Contest 112

C - Pyramid

https://atcoder.jp/contests/abc112/tasks/abc112_c

方針

N 個の情報のうち、少なくとも 1 つは h が正です。もし全ての h が 0 だったと仮定すると、 調査を行わなかった地点を中心座標とする高さ H = 1 のピラミッドが全て候補になり、「ピラミッドの中心座標と高さをちょうど 1 つに特定することができる」という制約に反するからです。

中心座標を  (iC_x, iC_y) とすると、h > 0 であった情報(x, y, h) を用いて  H = h + |x - iC_x| + |y - iC_y| で H が1つに決まります。あとは他の N - 1 個の情報がすべて h = \max(H - |x - iC_x| - |y - iC_y|, ~0) を満たすことを調べればよいです。 (iC_x, iC_y) の取りうる範囲は 0 以上 100 以下なので、全探索できます。

ソースコード

#include <bits/stdc++.h>
using namespace std;
typedef tuple<int,int,int> tup;

int main(){
    int N;  cin >> N;
    vector<tup> infs(N);
    for(int i=0; i<N; i++){
        int x, y, h;  cin >> x >> y >> h;
        infs[i] = tup(h,x,y);
    }
    sort(infs.begin(), infs.end());

    for(int iCx=0; iCx<=100; iCx++){
        for(int iCy=0; iCy<=100; iCy++){
            int h = get<0>(infs[N-1]);
            int x = get<1>(infs[N-1]);
            int y = get<2>(infs[N-1]);

            int H = h + abs(iCx-x) + abs(iCy-y);
            bool flag = true;
            for(int i=0; i<N-1; i++){
                int hi = get<0>(infs[i]);
                int xi = get<1>(infs[i]);
                int yi = get<2>(infs[i]);
                if(hi != max( H - abs(xi-iCx) - abs(yi-iCy) ,0)){
                    flag = false;
                    break;
                }
            }
            if(flag){
                cout << iCx << " " << iCy << " " << H << endl;
            }
        }
    }
}

D - Partition

https://atcoder.jp/contests/abc112/tasks/abc112_d

方針

k が  a_1, a_2, \dots, a_N の公約数のとき、k は M の約数です。また  a の各項を k で割ったときの商を  a^{\prime} とすると、 k(a_1^{\prime} + a_2^{\prime} + \dots + a_N^{\prime}) = M となりますが、ここで a^{\prime} の項はすべて 1 以上ですから、 a_1^{\prime} + a_2^{\prime} + \dots + a_N^{\prime} = M/k \geq N すなわち  k \leq M/N となります。

以上のことより、 M の約数 k の中で  k \leq M/N を満たす最大のものを出力すればよいです。

M の約数の求め方ですが、1 から M までのすべての整数について、M を割り切るか否かを調べていくと間に合いません。k が M の約数のとき M/k も M の約数であることを用いると、探索の範囲を 1 から  \sqrt{M} までに抑えることができます。

ソースコード

#include <bits/stdc++.h>
using namespace std;

int main(){
    int N, M;  cin >> N  >> M;
    vector<int> div; // M の約数を格納する。
    for(int i=1; i*i<=M; i++){
        if(M%i==0){
            div.push_back(i);
            div.push_back(M/i);
        }
    }

    sort(div.begin(), div.end());
    int n = div.size();
    for(int i=n-1; i>=0; i--){
        if(div[i] <= M/N){
            cout << div[i] << endl;
            return 0;
        }
    }
}

メモ

  • 上の方法で div に値を詰めていくと、 M が平方数で  M = k^2 と表されるとき、div に k が2個格納されてしまいます。今回は別にそれで問題ないです。