GoodCoder666的个人博客

LINE Verda Programming Contest (AtCoder Beginner Contest 263) A~E 题解

2022-08-07 · 12 min read
C++ 算法竞赛

A - Full House

题目大意

来自一个掼蛋爱好者的翻译qwq

给定一副扑克牌中五张牌的编号A,B,C,D,EA,B,C,D,E,判断这五张是否为一组“三带二”。(不懂的自行百度

数据范围:1A,B,C,D,E131\le A,B,C,D,E\le 13,且A,B,C,D,EA,B,C,D,E不会全部相同。

输入格式

A B C D EA~B~C~D~E

输出格式

如果是“三带二”,输出Yes;否则,输出No

样例

AA BB CC DD EE 输出
11 22 11 22 11 Yes
1212 1212 1111 11 22 No

分析

嘿嘿,被自己的翻译给笑喷了吓住了,从来没见过这么垃圾的翻译!
话不多说,这A题就是水(虽然studentWheat这位大佬还WA了3次,具体怎么WA的请大家好好学学引以为戒),解法很多,但是个人感觉还是直接统计一下来得简单明了,见代码。

代码

#include <cstdio>
using namespace std;

int cnt[13];

int main()
{
	for(int i=0; i<5; i++)
	{
		int x;
		scanf("%d", &x);
		cnt[--x] ++;
	}
	bool has2 = false, has3 = false;
	for(int i=0; i<13; i++)
		if(cnt[i] == 2) has2 = true;
		else if(cnt[i] == 3) has3 = true;
	puts(has2 && has3? "Yes": "No");
	return 0;
}

B - Ancestor

题目大意

NN个人,第ii个人的父/母是PiP_i,题目保证Pi<iP_i<i
问:第NN个人是第11个人的第几代?

2N502\le N\le 50
1Pi<i1\le P_i<i

输入格式

NN
P2 P3  PNP_2~P_3~\dots~P_N

输出格式

输出答案。

分析

本题可以使用DFS\text{DFS},但没有必要。题目限制条件特别给出了Pi<iP_i<i,因此如果按输入顺序依次处理每个人的世代,一个人的父亲肯定在这个人之前被考察到。

下面来看详细过程。
我们设depthi= \text{depth}_i=~ii个节点的深度,因此答案为depthN\text{depth}_N
初始时,depth0=0\text{depth}_0=0。对于i=1Ni=1\dots N,依次设置depthi:=depthPi+1\text{depth}_i:=\text{depth}_{P_i}+1
最终,输出结果即可。总时间复杂度为O(N)\mathcal O(N)

代码

#include <cstdio>
#define maxn 55
using namespace std;

int dep[maxn];

int main()
{
	int n;
	scanf("%d", &n);
	for(int i=1; i<n; i++)
	{
		int f;
		scanf("%d", &f);
		dep[i] = dep[--f] + 1;
	}
	printf("%d\n", dep[n - 1]);
	return 0;
}

C - Monotonically Increasing

题目大意

输出所有的长度为NN严格上升的序列,其中每个元素都在[1,M][1,M]之间,按字典升序输出,1NM101\le N\le M\le 10

输入格式

N MN~M

输出格式

字典升序输出所有符合条件的序列,一行一个,序列中的每个元素用空格分隔。

分析

基础的回溯算法题,见代码。

代码

#include <cstdio>
#define maxn 15
using namespace std;

int n, m, ans[maxn];

void dfs(int pos, int last)
{
	if(pos == n)
	{
		for(int i=0; i<n; i++)
			printf("%d ", ans[i]);
		putchar('\n');
		return;
	}
	while(++last <= m)
	{
		ans[pos] = last;
		dfs(pos + 1, last);
	}
}

int main()
{
	scanf("%d%d", &n, &m);
	dfs(0, 0);
	return 0;
}

D - Left Right Operation

题目大意

给定长为NN的整数序列A=(A1,A2,,AN)A=(A_1,A_2,\dots,A_N)
你可以进行下列两个操作,每个最多一次:

  • 选择整数xx,将AA的前xx项全部改成LL
  • 选择整数yy,将AA的后yy项全部改成RR

求操作后,最小的AA中所有元素之和。

1N2×1051\le N\le 2\times 10^5
109L,R,Ai109-10^9\le L,R,A_i\le 10^9

输入格式

N L RN~L~R
A1 A2  ANA_1~A_2~\dots~A_N

输出格式

输出答案。

分析

fi=(f_i=(使用操作11选择xix\le i时的前ii个序列元素的最小和))
    gi=(~~~~g_i=(使用操作22选择yiy\le i时的后ii个序列元素的最小和))
则可得递推式fi=min{fi1+Ai,L×i}f_i=\min\{f_{i-1}+A_i,L\times i\}gig_i同理。
此时,枚举两种操作的分界点ii,则答案为mini=1N(fi+gNi)\min\limits_{i=1}^N(f_i+g_{N-i})。实现时,可将gg数组倒过来计算,这样答案为mini=1N(fi+gi+1)\min\limits_{i=1}^N(f_i+g_{i+1})

递推式的正确性证明
前面已经提到了,fi=min{fi1+Ai,L×i}f_i=\min\{f_{i-1}+A_i,L\times i\}为什么?
先看min\min后面的部分,应该好理解,就是前ii个全部替换成LL的总和。前面的fi1+Aif_{i-1}+A_i才是关键。考虑fi1f_{i-1}的计算来源,要么是从fi2+Ai1f_{i-2}+A_{i-1}递推过来的,要么也是直接用L×(i1)L\times(i-1)得到的。再考虑fi2,fi3,,f1f_{i-2},f_{i-3},\dots,f_1会发现,递推式的结果一定是(一段LL)+(一段AiA_i)得到的。因此,这个递推式正确。gg的正确性也可以用同样的方法证明,感兴趣的读者可以自行尝试。

总时间复杂度为O(N)\mathcal O(N)

代码

注意使用long long

#include <cstdio>
#define maxn 200005
using namespace std;

using LL = long long;
inline LL min(const LL& x, const LL& y)
{
	return x < y? x: y;
}

int a[maxn];
LL f[maxn], g[maxn];

int main()
{
	int n, l, r;
	scanf("%d%d%d", &n, &l, &r);
	for(int i=0; i<n; i++)
		scanf("%d", a + i);
	f[0] = min(l, a[0]);
	for(int i=1; i<n; i++)
		f[i] = min(f[i - 1] + a[i], (i + 1LL) * l);
	for(int i=n-1; i>=0; i--)
		g[i] = min(g[i + 1] + a[i], LL(n - i) * r);
	LL ans = g[0];
	for(int i=0; i<n; i++)
		ans = min(ans, f[i] + g[i + 1]);
	printf("%lld\n", ans);
	return 0;
}

E - Sugoroku 3

题目大意

NN个方格,分别是方格11,方格22,..,方格NN
在方格1,2,,N11,2,\dots,N-1上,各有一枚骰子。方格ii上的骰子会按照相同的概率随机输出0,1,,Ai0,1,\dots,A_i中的一个。

直到到达方格NN之前,你每次会前进骰子输出的步数。换句话说,如果你在方格xx上(1x<N1\le x<N),骰子输出了数字yy0yAi0\le y\le A_i),你下一步会到达方格x+yx+y

求到达方格NN步数的期望值,对998244353998244353取模。

2N2×1052\le N\le 2\times 10^5
1AiNi1\le A_i\le N-i1i<N1\le i<N

有理数取模洛谷模板:P2613
任意一个有理数都可被表示为PQ\frac PQ的形式。令RR为取模的结果,则R×QP (mod 998244353)R\times Q\equiv P~(\bmod~998244353)
友情提示:对于除法计算,如AB\frac AB计算时,改为A×BP2modPA\times B^{P-2}\bmod P,其他逐步取模即可。本题中,P=998244353P=998244353

输入格式

NN
A1 A2  AN1A_1~A_2~\dots~A_{N-1}

输出格式

输出答案,对998244353998244353取模。

分析

dpi= \text{dp}_i=~iiNN的期望步数,则初始状态为dpN=0\text{dp}_N=0,答案为dp1\text{dp}_1
由于在方格上不能倒退,因此考虑倒序计算dp\text{dp}。对于第ii个点,易得

dpi=j=ii+AidpjAi+1+1\text{dp}_i=\frac{\sum\limits_{j=i}^{i+A_i}\text{dp}_j}{A_i+1}+1

其中,求和即遍历后面每一个下一步可能到达的位置,乘上出现概率1Ai+1\frac1{A_i+1},最后再加上11表示使用一步。
由于左右都有dpi\text{dp}_i,无法直接计算,我们将其单独提出并解方程,得:

dpi=(j=ii+Aidpj)+Ai+1Ai\text{dp}_i=\frac{(\sum\limits_{j=i}^{i+A_i}\text{dp}_j)+A_i+1}{A_i}

此时,时间复杂度为O(N2logP)\mathcal O(N^2\log P)(快速幂inv操作需要logP\log P的时间,其中P=998244353P=998244353),使用后缀和优化后可达O(NlogP)\mathcal O(N\log P),可以通过本题。

代码

#include <cstdio>
#define maxn 200005
#define MOD 998244353
using namespace std;

using LL = long long;
int a[maxn], suf[maxn];

inline int inv(LL x)
{
	LL res = 1LL;
	int p = MOD - 2;
	while(p)
	{
		if(p & 1) (res *= x) %= MOD;
		(x *= x) %= MOD, p >>= 1;
	}
	return res;
}

int main()
{
	int n, cur;
	scanf("%d", &n);
	for(int i=0; i<n-1; i++)
		scanf("%d", a + i);
	for(int i=n-2; i>=0; i--)
	{
		int t = suf[i + 1] - suf[i + 1 + a[i]];
		if(t < 0) t += MOD;
		cur = (a[i] + t + 1LL) % MOD * inv(a[i]) % MOD;
		if((suf[i] = suf[i + 1] + cur) >= MOD)
			suf[i] -= MOD;
	}
	printf("%d\n", cur);
	return 0;
}