比赛-训练赛 (15 Aug, 2018)
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;
}