1. 程式人生 > >比賽-訓練賽 (15 Aug, 2018)

比賽-訓練賽 (15 Aug, 2018)

next ring right %d 一個 set main scanf sort

1.) 2357數

單調隊列或者搜索都行。

#include <cstdio>
#include <deque>

using namespace std;

typedef long long ll;

deque<ll> A[4];

int main()
{
    ll N, x;
    scanf("%lld", &N);
    x = 1;
    while (x < N) {
        A[0].push_back(x * 2);
        A[1].push_back(x * 3);
        A[2].push_back(x * 5);
        A[3].push_back(x * 7);
        int p = 0;
        for (int i = 1; i <= 3; ++i) {
            if (A[i].front() < A[p].front())
                p = i;
        }
        x = A[p].front();
        for (int i = 0; i <= 3; ++i) {
            if (A[i].front() == A[p].front())
                A[i].pop_front();
        }
    }
    printf("%lld\n", x);
    return 0;
}

2.) 監獄

區間 DP 。枚舉區間內第一個被釋放的人來轉移狀態。

#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int INF = 1e9;
const int _N = 200;

int A[_N], B[15], f[_N][_N], N, M, ans;
bool mk[_N];

void test()
{
    int cnt = 0;
    for (int i = 1; i <= N; ++i)
        mk[i] = 0;
    for (int i = 1; i <= M; ++i) {
        int j;
        mk[A[B[i]]] = 1;
        for (j = A[B[i]] - 1; j >= 1 && !mk[j]; --j);
        cnt += A[B[i]] - 1 - j;
        for (j = A[B[i]] + 1; j <= N && !mk[j]; ++j);
        cnt += j - 1 - A[B[i]];
    }
    ans = min(ans, cnt);
    return;
}

void fun1()
{
    for (int i = 1; i <= M; ++i)
        B[i] = i;
    ans = INF;
    test();
    while (next_permutation(B + 1, B + 1 + M))
        test();
    printf("%d\n", ans);
    return;
}

int dfs(int l, int r)
{
    if (f[l][r] != -1) {
        return f[l][r];
    }
    if (l == r) {
        return f[l][r] = 0;
    }
    f[l][r] = INF;
    for (int i = l; i <= r; ++i) {
        f[l][r] = min(f[l][r], dfs(l, i) + dfs(i + 1, r) + A[r] - A[l - 1] - 2);
    }
//  printf("[%d, %d] = %d\n", l, r, f[l][r]);
    return f[l][r];
}

int main()
{
    
    scanf("%d%d", &N, &M);
    for (int i = 1; i <= M; ++i) {
        scanf("%d", &A[i]);
    }
    if (N <= 105 && M <= 6) {
        fun1();//O(M! * N * M)
        return 0;
    }
    sort(A + 1, A + 1 + M);
    A[0] = 0, A[M + 1] = N + 1;
    memset(f, -1, sizeof f);
    printf("%d\n", dfs(1, M + 1));
    return 0;
}

3.) lucknum

數位 DP 可以拿到 60 分。註意到 DP 方程和組合數的遞推式很像。考慮把 \(n\) 個數位作為球,把這些球扔到 \(m\) 表示 \([0, m-1]\)\(m\) 個數字的桶裏去(或分成 \(m\) 份)。扔完後按桶表示的數字大小從小到大排序,把扔到桶內的球對應的數位上的數,設置為桶表示的數字,就得到了一個合理的 lucknum 。桶內可以為空。隔板法搞一下答案就是 \(\left(_{m-1}^{n+m-1}\right)\)

#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

typedef long long ll;

ll N, M, P;

ll mont(ll t1, ll t2)
{
    t1 %= P;
    ll t = 1;
    while (t2) {
        if (t2 & 1)
            t = t * t1 % P;
        t2 >>= 1;
        t1 = t1 * t1 % P;
    }
    return t;
}

int main()
{
    ll dn, up;
    scanf("%lld%lld%lld", &N, &M, &P);
    dn = M + N - 1;
    up = M - 1;
    if (up > dn - up)
        up = dn - up;
    ll t0 = 1, t1 = 1;
    for (ll i = 1; i <= up; ++i) {
        t0 = t0 * i % P;
        t1 = t1 * (dn - i + 1) % P;
    }
    printf("%lld\n", t1 * mont(t0, P - 2) % P);
    return 0;
}

比賽-訓練賽 (15 Aug, 2018)