A
给定一棵 n 个点的有根树,节点编号为 1~n,根节点为 1。你会不停地重复 以下操作,直到所有点被覆盖为止:等概率随机树上的一个未被覆盖的点,并 覆盖这个点到根路径上的所有点。请问你的期望操作次数是多少?为了避免高 精度运算,你只需要输出答案对 998244353 取模后的结果。
概率期望经典入门题,根据期望的线性性,答案就是每个点被选到的期望之和,每个点的贡献为一,所以只需要求每个点被选到的概率即可,每个点必须在它的子树中的点被选之前选,所以概率是
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MOD = 998244353;
const int N = 10000000;
int n, head[N + 50], num, inv[N + 50], siz[N + 50], f[N + 50];
void Read(int &x)
{
x = 0; int p = 0; char st = getchar();
while (st < '0' || st > '9') p = (st == '-'), st = getchar();
while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
x = p ? -x : x;
return;
}
struct Node
{
int next, to;
} edge[N * 2 + 50];
void Addedge(int u, int v)
{
edge[++num] = (Node){head[u], v};
head[u] = num;
return;
}
int main()
{
Read(n);
inv[1] = 1;
for (int i = 2; i <= n; i++) Read(f[i]), inv[i] = 1LL * (MOD - MOD / i) * inv[MOD % i] % MOD;
for (int i = n; i >= 1; i--) siz[i]++, siz[f[i]] += siz[i];
int ans = 0;
for (int i = 1; i <= n; i++) (ans += inv[siz[i]]) %= MOD;
printf("%d", ans);
return 0;
}
B
有 n 颗糖果,编号为 1~n,每颗糖果有美味度 和美观度 。请你从中取 出不超过 颗糖果,使得这些糖果的美味度之和大于所有糖果美味度总和 的一半,且美观度之和大于所有糖果美观度总和的一半,或判断无解。
这道题的数学题我做过!那道题里它说有199个a,199个b!类似,对于偶数,随便取出来一个使得剩下的变成奇数个。
对于奇数,按a排序,先取a最大的一个,然后剩下的从前往后两两分为一组,每组选b较大的一个。
这样一定是正确的因为每组b都选了较大的,最劣情况下选的和没选的相同,但是另外还选了一个元素,所以b的总和一定大于所有的b的一半;因为是按照a排序两两一组,最劣情况下每组都选了较小的a,但是因为我们已经选了最大的a,就相当于可以给元素重新分组,从后往前两两一组,这样就可以和b一样证明。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100000;
int n;
struct Node
{
int x, y, id;
} a[N + 50];
int Cmp(Node a, Node b)
{
return a.x < b.x;
}
void Read(int &x)
{
x = 0; int p = 0; char st = getchar();
while (st < '0' || st > '9') p = (st == '-'), st = getchar();
while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
x = p ? -x : x;
return;
}
int main()
{
Read(n);
for (int i = 1; i <= n; i++) Read(a[i].x), a[i].id = i;
for (int i = 1; i <= n; i++) Read(a[i].y);
printf("%d\n", n / 2 + 1);
sort(a + 1, a + n + 1, Cmp);
if (n % 2 == 0) printf("%d ", a[n].id), n--;
printf("%d ", a[n].id); n--;
for (int i = 1; i <= n - 1; i += 2) printf("%d ", a[i].y > a[i + 1].y ? a[i].id : a[i + 1].id);
return 0;
}
C
给定一棵 n 个点的有根树,节点编号为 1~n,根节点为 1。最开始所有点都 是白色,你需要选择一些点染成黑色,同时有若干条限制,限制有两种:
A 类限制:x 的子树内至少有 y 个点被染成黑色
B 类限制:x 的子树外至少有 y 个点被染成黑色 请问最少把多少个点染成黑色才能满足条件,如果无解输出-1。
第一种限制可以求出每个点子树内染色的下限,第二种限制通过二分可以求出每个点子树内染色的上限,二分判定即可。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 100000;
const int INF = 0x7fffffff;
int n, head[N + 50], num, siz[N + 50], minn[N + 50], maxx[N + 50], mid, flag, xx[N + 50][2], mm[N + 50];
struct Node
{
int next, to;
} edge[N * 2 + 50];
void Addedge(int u, int v)
{
edge[++num] = (Node){head[u], v};
head[u] = num;
return;
}
void Read(int &x)
{
x = 0; int p = 0; char st = getchar();
while (st < '0' || st > '9') p = (st == '-'), st = getchar();
while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
x = p ? -x : x;
return;
}
void Dfs(int x, int fa)
{
siz[x] = 1;
for (int i = head[x]; i; i = edge[i].next)
{
int v = edge[i].to;
if (v == fa) continue;
Dfs(v, x);
siz[x] += siz[v];
}
return;
}
void Dfspd(int x, int fa)
{
mm[x] = mid - xx[x][1];
int tmp = 1;
for (int i = head[x]; i; i = edge[i].next)
{
int v = edge[i].to;
if (v == fa) continue;
Dfspd(v, x); tmp += mm[v];
}
mm[x] = min(mm[x], tmp);
if (mm[x] < xx[x][0]) flag = 0;
return;
}
void Dfs2(int x, int fa)
{
int tmp = 0;
for (int i = head[x]; i; i = edge[i].next)
{
int v = edge[i].to;
if (v == fa) continue;
Dfs2(v, x);
tmp += xx[v][0];
}
xx[x][0] = max(tmp, xx[x][0]);
return;
}
int main()
{
Read(n);
for (int i = 1, u, v; i <= n - 1; i++)
{
Read(u); Read(v);
Addedge(u, v); Addedge(v, u);
}
int a, b;
Dfs(1, 0);
Read(a);
for (int i = 1; i <= a; i++)
{
int x, y;
Read(x); Read(y);
xx[x][0] = max(xx[x][0], y);
if (y > siz[x]) { puts("-1"); return 0; }
}
Read(b);
for (int i = 1; i <= b; i++)
{
int x, y;
Read(x); Read(y);
xx[x][1] = max(xx[x][1], y);
if (y > n - siz[x]) { puts("-1"); return 0; }
}
Dfs2(1, 0);
int l = 0, r = n + 50;
while (l < r)
{
mid = (l + r) >> 1;
flag = 1;
Dfspd(1, 0);
if (flag) r = mid;
else l = mid + 1;
}
printf("%d\n", l);
return 0;
}
D
给定一棵 n 个点的有根树,节点编号为 1~n,根节点为 1,满足每个点的儿 子个数为 0 或 2 个。Alice 和 Bob 在这棵树上玩游戏,Alice 控制了所有到根距离 为偶数的非叶节点,Bob 控制了所有到根距离为奇数的非叶节点(距离就是两 点间的边数)。 游戏开始时,每个叶子节点 u 都会被分配一个二元组(c(u),d(u)),c 和 d 均为 [1,k]中的正整数。接下来 Alice 和 Bob 会分别对他们控制的每一个节点指定一个 重儿子,显然从根节点顺着重链会走到唯一一个叶结点 x,这时 Alice 的得分为 c(x),Bob 的得分为 d(x)。称两人的一种选择方案为一种策略,设叶子结点有 l 个,显然策略有种。 定义一种策略是均衡的,当且仅当 Alice 不改变方案时,Bob 无论怎么改变 自己的方案都无法使自己的得分变大;且 Bob 不改变方案时,Alice 无论怎么改 变自己的方案也无法使自己的得分变大。请问对于所有合法的初始叶结点权 值,均衡的策略的个数总和是多少?答案对 998244353 取模。
先转移出来可以随便修改决策时,两个人分别拿到最多i分的方案数。
然后注意到如果钦定一条重链,两个人的决策其实是独立的,所以再根据上面的东西转移出来走到x点时,每个人最后得分为i的决策数,在叶子节点相乘即可。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 5000;
const int MOD = 998244353;
int n, k, g1[N + 50][N + 50], g2[N + 50][N + 50], bin[N + 50], ans, f1[N + 50][N + 50], f2[N + 50][N + 50];
struct Node
{
int son[2];
} tr[N + 50];
void Addedge(int u, int v)
{
if (!tr[u].son[0]) tr[u].son[0] = v;
else tr[u].son[1] = v;
return;
}
void Read(int &x)
{
x = 0; int p = 0; char st = getchar();
while (st < '0' || st > '9') p = (st == '-'), st = getchar();
while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
x = p ? -x : x;
return;
}
void Dfs1(int x, int y)
{
if (!tr[x].son[0])
{
for (int i = 1; i <= k; i++) g1[x][i] = g2[x][i] = i * k;//显然小于等于i的得分下该部分可以有i种选择,另外一部分随便选择。
bin[x] = k * k;
return;
}
Dfs1(tr[x].son[0], y ^ 1); Dfs1(tr[x].son[1], y ^ 1);//记录当前节点由谁控制。
int s0 = tr[x].son[0], s1 = tr[x].son[1];
bin[x] = 1LL * bin[s0] * bin[s1] % MOD * 2 % MOD;
if (y)
{
for (int i = 1; i <= k; i++)
g2[x][i] = 1LL * g2[s0][i] * g2[s1][i] % MOD * 2 % MOD, //乘2是因为要从两个儿子中选择一个当作重儿子,两个都得小于等于i是因为决策点可以任意改变。
g1[x][i] = (0LL + 1LL * g1[s0][i] * bin[s1] % MOD + 1LL * g1[s1][i] * bin[s0] % MOD) % MOD; //另外一个只能被钦定是走右边还是走左边,除了钦定的以外的另外一边因为觉决策点不会改变所以可以随便走。
}
else
{
for (int i = 1; i <= k; i++)
g1[x][i] = 1LL * g1[s0][i] * g1[s1][i] % MOD * 2 % MOD,
g2[x][i] = (0LL + 1LL * g2[s0][i] * bin[s1] % MOD + 1LL * g2[s1][i] * bin[s0] % MOD) % MOD;
}
return;
}
void Dfs2(int x, int y)
{
if (!tr[x].son[0])
{
int tmp1 = 0, tmp2 = 0;
for (int i = 1; i <= k; i++) tmp1 = (0LL + tmp1 + f1[x][i]) % MOD, tmp2 = (0LL + tmp2 + f2[x][i]) % MOD;
ans = (0LL + ans + 1LL * tmp1 * tmp2 % MOD) % MOD;
return;
}
int s0 = tr[x].son[0], s1 = tr[x].son[1];
if (y)
{
for (int i = 1; i <= k; i++)
f2[s0][i] = 1LL * f2[x][i] * g2[s1][i] % MOD, f2[s1][i] = 1LL * f2[x][i] * g2[s0][i] % MOD,//可以执行决策的另外一边随便选。
f1[s0][i] = f1[x][i], f1[s1][i] = f1[x][i];//因为这里是独立的,所以不能执行决策就直接赋值。
}
else
{
for (int i = 1; i <= k; i++)
f1[s0][i] = 1LL * f1[x][i] * g1[s1][i] % MOD, f1[s1][i] = 1LL * f1[x][i] * g1[s0][i] % MOD,
f2[s0][i] = f2[x][i], f2[s1][i] = f2[x][i];
}
Dfs2(tr[x].son[0], y ^ 1); Dfs2(tr[x].son[1], y ^ 1);
return;
}
int main()
{
Read(n); Read(k);
for (int i = 2, fa; i <= n; i++) Read(fa), Addedge(fa, i);
Dfs1(1, 0);
for (int i = 1; i <= k; i++) f1[1][i] = f2[1][i] = 1;//这里两个人的决策是独立的。
Dfs2(1, 0);
printf("%d", ans);
return 0;
}
记得点赞加关注哦!