ABC 393

D - Swap to Gather

なんとなく中央の 1 に寄せると良さそうという雰囲気で実装して AC してしまった

void solve() {
    int N;
    string S;
    cin >> N >> S;

    vint ids;
    rep(i, N) {
        if (S[i] == '1')
            ids.push_back(i);
    }

    ll ans = 0;
    int mid = ids[ids.size() / 2];
    int zid = mid + 1;
    for (int i = mid + 1; i < N; i++) {
        while (S[zid] == '1' && zid < N)
            zid++;
        if (i <= zid)
            continue;
        if (S[i] == '1') {
            ans += i - zid;
            swap(S[i], S[zid]);
        }
    }

    zid = mid - 1;
    for (int i = mid - 1; i >= 0; i--) {
        while (S[zid] == '1' && zid >= 0)
            zid--;
        if (zid <= i)
            continue;
        if (S[i] == '1') {
            ans += zid - i;
            swap(S[i], S[zid]);
        }
    }
    cout << ans << endl;
    // cout << S << endl;
}

E - GCD of Subset

愚直に全ての $A_i$ についての約数を調べて出現回数をメモって、各 $i$ に対して $A_i$ の約数のうち出現回数が $K$ を超えている中で最大のものを出力とすると TLE ぎりぎりだがなんとか AC できた。 しかし、どうやら想定解ではなかったらしい。

実際の提出コード

vll sieve((int)1e6 + 5, -1);

vector<pair<int, int>> factor(int x) {
    vector<pair<int, int>> res;
    while (x != 1) {
        ll cnt = 0;
        ll d = sieve[x];
        while (x % d == 0) {
            x /= d;
            cnt++;
        }
        res.push_back({d, cnt});
    }

    return res;
}

vvint memo((int)1e6 + 5);

vint divs(int x) {
    if (memo[x].size() != 0)
        return memo[x];
    auto facs = factor(x);
    vint res = {1};
    for (auto [d, cnt] : facs) {
        int n = res.size();
        rep(i, n) {
            int m = 1;
            rep(j, cnt) {
                m *= d;
                res.push_back(res[i] * m);
            }
        }
    }

    return memo[x] = res;
}

void solve() {
    int n, k;
    cin >> n >> k;
    vint a(n);
    rep(i, n) cin >> a[i];

    for (int i = 2; i < (int)1e6 + 5; i++) {
        for (int j = i; j < (int)1e6 + 5; j += i) {
            if (sieve[j] == -1)
                sieve[j] = i;
        }
    }

    vint cnt((int)1e6 + 5, 0);
    for (int x : a) {
        vint v = divs(x);
        for (int y : v)
            cnt[y]++;
    }

    vint sorted((int)1e6 + 5, 0);

    for (int x : a) {
        if (sorted[x])
            continue;
        sort(rall(memo[x]));
        sorted[x] = 1;
    }

    for (int x : a) {
        for (int y : memo[x]) {
            if (cnt[y] >= k) {
                cout << y << endl;
                break;
            }
        }
    }
}

模範

void solve() {
    int n, k;
    cin >> n >> k;

    vint a(n);
    rep(i, n) cin >> a[i];

    int mx = *max_element(all(a));
    vint num(mx + 1);

    for (int x : a)
        num[x]++;

    vint cnt(mx + 1, 0);
    rep2(i, 1, mx + 1) {
        for (int j = i; j <= mx; j += i) {
            cnt[i] += num[j];
        }
    }

    vint ans(mx + 1, 0);
    rep2(i, 1, mx + 1) {
        if (cnt[i] >= k) {
            for (int j = i; j <= mx; j += i) {
                ans[j] = i;
            }
        }
    }

    for (int x : a)
        cout << ans[x] << endl;
}

F - Prefix LIS Query

普通の LIS をしつつ $R_i$ が昇順の順でクエリを処理する。

void solve() {
    int n, q;
    cin >> n >> q;
    vint a(n);
    rep(i, n) cin >> a[i];
    vector<tuple<int, int, int>> qs;
    rep(i, q) {
        int r, x;
        cin >> r >> x;
        qs.push_back({r, x, i});
    }

    sort(all(qs));

    vint lis(n + 1, INF);
    int k = 0;  // LIS の計算で使う要素の index
    vint ans(q);
    for (auto [r, x, id] : qs) {
        for (int i = k; i < r; i++) {
            *lower_bound(all(lis), a[i]) = a[i];
        }
        k = r;

        int d = upper_bound(all(lis), x) - lis.begin();
        ans[id] = d;
    }

    for (int x : ans)
        cout << x << endl;
}