多校B层冲刺NOIP20211111模拟12

题面:PDFhttp://xn--gwt928b.accoders.com/pdf/10248/10248.pdficon-default.png?t=LA92http://xn--gwt928b.accoders.com/pdf/10248/10248.pdf

A

给定一棵 n 个点的有根树,节点编号为 1~n,根节点为 1。你会不停地重复 以下操作,直到所有点被覆盖为止:等概率随机树上的一个未被覆盖的点,并 覆盖这个点到根路径上的所有点。请问你的期望操作次数是多少?为了避免高 精度运算,你只需要输出答案对 998244353 取模后的结果。

概率期望经典入门题,根据期望的线性性,答案就是每个点被选到的期望之和,每个点的贡献为一,所以只需要求每个点被选到的概率即可,每个点必须在它的子树中的点被选之前选,所以概率是\frac{1}{size_i}

#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,每颗糖果有美味度 a_i 和美观度 b_i。请你从中取 出不超过 \left \lfloor \frac{n}{2} \right \rfloor+1 颗糖果,使得这些糖果的美味度之和大于所有糖果美味度总和 的一半,且美观度之和大于所有糖果美观度总和的一半,或判断无解。

这道题的数学题我做过!那道题里它说有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 个,显然策略有2^{n-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;
}

记得点赞加关注哦!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
NOIP(全国青少年信息学奥林匹克竞赛)是中国国内最高水平的信息学竞赛之一,是计算机领域的重要赛事。针对NOIP的模拟题,通常是为了帮助参赛者熟悉比赛形式和题型,提供足够的训练机会。 数据下载是NOIP比赛中的一个重要环节,因为参赛者需要根据提供的数据集进行程序开发和测试。数据下载一般通过网络进行,参赛者需要在指定的时间段内下载数据集到自己的计算机上。 在进行NOIP模拟题数据下载时,我们可以按照以下步骤进行操作: 1. 确认下载链接:NOIP官方会提供下载数据集的链接或者FTP地址,参赛者需要确认链接是否可用和正确。 2. 选择下载工具:根据自己的需求,参赛者可以选择合适的下载工具进行操作。常见的下载工具有浏览器内置下载工具、迅雷、IDM等,可以根据个人的习惯和需求选择合适的下载工具。 3. 打开下载工具:根据所选择的下载工具类型,打开对应的软件,进入下载界面。 4. 输入下载链接:将NOIP提供的数据集下载链接复制粘贴到下载工具的链接输入框中,点击确定或开始进行下载。 5. 等待下载完成:根据数据集的大小和网络速不同,下载时间会有所变化。参赛者需要耐心等待下载完成,确保数据集完整地保存到自己的计算机上。 6. 验证数据完整性:下载完成后,参赛者需要验证数据集的完整性,确保所有文件都成功地保存到指定位置。可以进行文件大小的比对或者逐个文件的校验来检查数据完整性。 通过以上步骤,参赛者可以成功地进行NOIP模拟题数据的下载。在实际比赛中,一个高效的数据下载过程可以提高参赛者的准备时间和竞争力,确保能够充分利用所提供的数据集进行开发和测试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lvshu · 绿树

非常感谢您的搭讪

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值