比赛-sparrow学长的训练赛 (21 Aug, 2018)
A. ‘+’ 和 ‘√’##
考虑第 \(k\) 轮时应把数字 \(x_k\) 增加到 \(t_k\) ,使得 \(\sqrt{t_k}\) 是 \(k + 1\) 倍数,同时 \(t_k\) 本身是 \(k\) 的倍数。即 \(t_k = f \cdot (k + 1)^2 \cdot k\) , \(f\) 是整数。由于 \(t_k\) 也是一个完全平方数,所以考虑直接让 \(f = k\) ,则 \(t_k = ((k + 1) \cdot k)^2\) 。这样在 \(n = 100000\) 时,最大的答案似乎是 \(16\) 位的,没有爆掉,就很好,水过去了。
#include <cstdio>
#include <ctype.h>
#include <stack>
using namespace std;
typedef long long ll;
char buf[1<<20], *p1, *p2;
#define GC (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++)
template<typename T>
void rd(T &num)
{
char tt;
while (!isdigit(tt = GC));
num = tt - '0';
while (isdigit(tt = GC))
num = num * 10 + tt - '0';
return;
}
template<typename T>
void pt(T num)
{
stack<char> SSS;
do SSS.push(num % 10 + '0');
while (num /= 10);
while (!SSS.empty())
putchar(SSS.top()), SSS.pop();
putchar('\n');
return;
}
ll N;
int main()
{
rd(N);
ll X = 2, K = 1;
for (int i = 1; i <= N; ++i) {
pt(K * (K + 1) * (K + 1) - X / K);
X = K * (K + 1);
++K;
}
return 0;
}
B. DFS 序##
考虑在节点 \(p\) 时,向没有遍历过的节点继续搜索。下一步可以是若干节点中的任意一个,因此可以用 next_permutation()
枚举一下遍历顺序。这样可以拿到暴力分 20 。正解是 DP 和记忆化搜索。 状态 \(f_{p, S}\) 表示在讨论完 \(S\) 集合的节点,当前在 \(p\) 号节点时,继续遍历整张图的总方案数。枚举 \(p\) 关联的不在 \(S\) 集合的点 \(i\) 。如果接下来向 \(i\) 遍历,再次回溯到 \(p\) 时,显然已经遍历完了 \(i\) 所在的与 \(S\) 没有交集的那部分强联通分量 \(T\) 。因此之后的遍历是以 \(S \cup T\) 为新的 \(S'\) , \(p\) 为新的 \(p'\) 进行的。选择向 \(i\) 遍历这一方案,和遍历完之后回溯到 \(p\) 再向其他节点遍历这一方案,互不影响。根据乘法原理:
\[f(p,S) = \sum f(p, S \cup T) \cdot f(i, S \cup i)
\]
求 \(T\) 可以提前预处理,也可以在搜索中暴力 BFS 或者 DFS 。
#include <stdio.h>
#include <queue>
#include <string.h>
using namespace std;
typedef long long ll;
ll ans, f[1 << 14][15];
int N;
char str[20];
bool G[20][20];
ll dfs(int s, int p)
{
if (~f[s][p]) return f[s][p];
f[s][p] = 0;
for (int i = 1; i <= N; ++i) {
if (G[p][i] && ((s >> (i - 1)) & 1 ^ 1)) {
int t = 1 << (i - 1);
queue<int> Q;
Q.push(i);
while (!Q.empty()) {
int tmp = Q.front();
Q.pop();
for (int g = 1; g <= N; ++g)
if (G[tmp][g] && ((s >> (g - 1)) & 1 ^ 1) && ((t >> (g - 1)) & 1 ^ 1)) {
t |= 1 << (g - 1), Q.push(g);
}
}
f[s][p] += dfs(s | t, p) * dfs(s | (1 << (i - 1)), i);
}
}
f[s][p] = max(f[s][p], 1ll);
return f[s][p];
}
int main()
{
scanf("%d", &N);
for (int i = 1; i <= N; ++i) {
scanf("%s", str + 1);
for (int j = 1; j <= N; ++j)
G[i][j] = str[j] == 'Y';
}
memset(f, -1, sizeof f);
for (int i = 1; i <= N; ++i)
ans += dfs(1 << (i - 1), i);
printf("%lld\n", ans);
return 0;
}
C. 基因##
据说考点是 AC 自动机和 DP , 以及矩阵乘法(蒟蒻乘法)。