# 「csp校内训练 2019-10-24」解题报告

## T1、猴猴吃苹果

### $Solution$：

$K$ 为根，每一次取一个叶子到根的路径；

$f_i$ 表示 $i$ ( $i$ 是叶子) 能够删掉的最远的节点，按 $f_i$ 和编号排序即可。

### $Source$：

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>
int in() {
int x = 0; char c = getchar(); bool f = 0;
while (c < '0' || c > '9')
f |= c == '-', c = getchar();
while (c >= '0' && c <= '9')
x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return f ? -x : x;
}
template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }

const int N = 5e4 + 5;

std::vector<int> ver[N];
int tmp[N];
int len[N], lef[N];
int n, rt;
int f[N], id[N];

inline bool cmp(const int &i, const int &j) {
if (f[i] == f[j])
return i < j;
return f[i] > f[j];
}
inline void jb(const int u, const int v) {
ver[u].push_back(v), ver[v].push_back(u);
}

void dfs(const int u, const int fa = -1) {
len[u] = 1, lef[u] = u;
for (unsigned i = 0; i < ver[u].size(); ++i) {
int v = ver[u][i];
if (v == fa)
continue;
dfs(v, u);
if (len[u] < len[v] + 1 || (len[u] == len[v] + 1 && lef[u] > lef[v]))
len[u] = len[v] + 1, lef[u] = lef[v];
}
chk_max(f[lef[u]], len[u]);
}

int main() {
//freopen("in", "r", stdin);
freopen("apple.in", "r", stdin);
freopen("apple.out", "w", stdout);
n = in(), rt = in() + 1;
if (n == 1)
return puts("0"), 0;
for (int i = 2; i <= n; ++i)
jb (i, in() + 1);
dfs(rt);
for (int i = 1; i <= n; ++i)
id[i] = i;
std::sort(id + 1, id + 1 + n, cmp);
printf("%d\n", rt - 1);
for (int i = 1; i <= n; ++i)
if (f[id[i]])
printf("%d\n", id[i] - 1);
return 0;
}


## T2、猴猴吃香蕉

(不要写下面的铁憨憨写法)

### $Source$：

#include <cstdio>
#include <cstring>
#include <algorithm>
int in() {
int x = 0; char c = getchar(); bool f = 0;
while (c < '0' || c > '9')
f |= c == '-', c = getchar();
while (c >= '0' && c <= '9')
x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return f ? -x : x;
}
template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }

const int N = 1e3 + 5, mod = 1e9 + 7;

int pri[10005], pcnt;
int n, m, nn;
int a[N], s[N], p[101], pre[101], c[101];

void init() {
for (int i = 2, j; i <= 10000; ++i) {
for (j = 2; j * j <= i; ++j)
if (i % j == 0)
break;
if (j * j > i)
pri[++pcnt] = i;
}
}

void prep() {
memset(c, 0, sizeof(c));
memset(s, 0, sizeof(s));
nn = 0;
for (int i = 1; i <= pcnt && pri[i] * pri[i] <= m; ++i) {
if (m % pri[i] == 0) {
p[++nn] = pri[i];
while (m % pri[i] == 0)
m /= pri[i], ++c[nn];
}
}
if (m != 1)
p[++nn] = m, c[nn] = 1;
pre[0] = 1;
for (int i = 1; i <= nn; ++i)
pre[i] = pre[i - 1] * (c[i] + 1);
for (int i = 1, tmp; i <= n; ++i)
for (int j = 1; j <= nn; ++j) {
tmp = 0;
while (a[i] % p[j] == 0)
a[i] /= p[j], ++tmp;
s[i] += pre[j - 1] * tmp;
}
//for (int i = 1; i <= n; ++i)
//    printf("%d\n", s[i]);
}

int dp() {
static int f[N];
memset(f, 0, sizeof(f));
f[0] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = pre[nn] - s[i] - 1, k; j >= 0; --j) {
for (k = 1; k <= nn; ++k)
if (s[i] % pre[k] / pre[k - 1] + j % pre[k] / pre[k - 1] > c[k])
break;
if (k <= nn)
continue;
f[j + s[i]] += f[j];
if (f[j + s[i]] >= mod)
f[j + s[i]] -= mod;
}
}
return f[pre[nn] - 1];
}

int main() {
//freopen("in", "r", stdin);
freopen("banana.in", "r", stdin);
freopen("banana.out", "w", stdout);
init();
int T = in();
while (T--) {
n = in(), m = in();
for (int i = 1; i <= n; ++i) {
a[i] = in();
if (m % a[i])
--i, --n;
}
prep();
printf("%d\n", dp());
}
return 0;
}


## T3、猴猴的比赛

### $Solution$：

$dfs$ 遍历第二棵树，每个点对同时在两棵子树内的点有贡献；

### $Source$：

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
int in() {
int x = 0; char c = getchar(); bool f = 0;
while (c < '0' || c > '9')
f |= c == '-', c = getchar();
while (c >= '0' && c <= '9')
x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return f ? -x : x;
}
template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }

const int N = 1e5 + 5;

struct edge {
int next, to;
} e[N << 2];
int ecnt = 1, head[N << 1];
int n;
long long res;

struct binary_index_tree {
int t[N];
void modify(int p, int k) { for (; p <= n; p += (p & -p)) t[p] += k; }
int ask(int p, int ret = 0) { for (; p; p -= (p & -p)) ret += t[p]; return ret; }
} bit;

int dfn[N], siz[N];
void dfs1(const int u, const int fa = -1) {
siz[u] = 1;
dfn[u] = ++dfn[0];
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].to;
if (v == fa)
continue;
dfs1(v, u);
siz[u] += siz[v];
}
}

void dfs2(const int u, const int fa = -1) {
bit.modify(dfn[u - n], 1), bit.modify(dfn[u - n] + siz[u - n], -1);
for (int i = head[u]; i; i = e[i].next)
if (e[i].to != fa)
dfs2(e[i].to, u);
bit.modify(dfn[u - n], -1), bit.modify(dfn[u - n] + siz[u - n], 1);
}

int main() {
freopen("in", "r", stdin);
//freopen("climb.in", "r", stdin);
//freopen("climb.out", "w", stdout);
n = in();
for (int i = 1, x, y; i < n; ++i) {
x = in(), y = in();