【Codeforces】Codeforces Round #829 (Div. 2) A-E

188 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第24天,点击查看活动详情

时隔好久又一次出 E 了qwq!

题目链接

Dashboard - Codeforces Round #829 (Div. 2) - Codeforces

A Technical Support

题目大意

您在一家大公司的技术支持质量控制部门工作。您的工作是确保所有客户问题都已得到解决。

今天,您需要检查客户和技术支持经理之间的聊天记录。根据工作规则,客户的每条消息都回答。然而,有时客户提出问题的速度如此之快,以至于经理对旧问题的一些回答出现在客户提出一些新问题之后。

由于隐私政策,您无法获得消息的全文,只能看到消息的顺序以及每条消息的类型:客户问题或技术支持经理的回复。保证对话以客户的问题开始。

您必须确定,此对话是否符合上述工作规则。

思路

判断每个“Q”后面是否都有对应的“A”,即任意时刻后缀“A"的数量不少于后缀“Q”的数量,倒序遍历字符串统计一下即可。

代码

#include <bits/stdc++.h>
#define nnn printf("No\n")
#define yyy printf("Yes\n")
using namespace std;
const int N=500001;
char ch[N];
int n;
int solve()
{
	scanf("%d",&n);
	cin>>ch+1;
	int Q=0,A=0;
	for (int i=n;i>=1;--i)
	{
		if (ch[i]=='Q') Q++;
		else A++;
		if (A<Q) return 0;
	}
	return 1;
}
int main()
{
	int T=1;
	for (scanf("%d",&T);T--;)
                if (solve()) yyy;
                else nnn;
	return 0;
}

B Kevin and Permutation

题目大意

求一个 1 到 nn 的排列,要求相邻两个元素之差的最小值最大。

思路

易知一个 1 到 nn 的排列相邻两个元素之差的最小值最大是 12\frac{1}{2}

一个正确的构造方法是 n2+1,1,n2+2,2,...\frac{n}{2}+1,1,\frac{n}{2}+2,2,...

代码

#include <bits/stdc++.h>
using namespace std;
int n,m,k;
int solve()
{
	scanf("%d",&n);
	for (int i=1,j=n/2+1;j<=n;++i,++j)
	{
		printf("%d ",j);
		if (i<=n/2) printf("%d ",i);
	}
	printf("\n");
        return 0;
}
int main()
{
	int T=1;
	for (scanf("%d",&T);T--;) solve();
	return 0;
}

C1+C2 Make Nonzero Sum

题目大意

给定长度为 nn 的数组 a1,a2,...,ana_1,a_2,...,a_n,元素的取值仅有可能是 1,0,1-1,0,1

将整个数组划分成若干段,第 kkalk,alk+1,...,ark1,arka_{l_k},a_{{l_k}+1},...,a_{r_k-1},a_{r_k} 的权值 sk=alkalk+1+alk+2alk+3...±arks_k=a_{l_k}-a_{{l_k}+1}+a_{{l_k}+2}-a_{{l_k}+3}...\pm a_{r_k}

输出任何一种使得所有划分的区间的权值和恰好为 0 的划分方案,无解输出 -1。

思路

没看懂 C1 有什么特殊之处……读了 C1 之后写了一发发现可以把 C2 过掉……

由于不需要最小化划分出的段的数量,一个长度为 3 的段可以分成一个长度为 2 的段和一个长度为 1 的段,一个长度为 4 的段可以分成 2 个长度为 2 的段……不影响最终的权值和。即我们划分出的段的长度最长为 2。

我们先假设把原数组划分成 nn 段,即每一个元素划分一段,我们只需要考虑让哪些段和前一个段合并,形成长度为 2 的段即可。则原问题转化为从 2 到 nn 中选择若干个不相邻的下标让其中的元素乘上 -1,使得数组中所有元素的和为 0。而在我们数组中的元素取值仅有可能是 1,0,1-1,0,1 的情况下:

  • 让一个 1 乘 -1,则使得所有元素之和减少 2
  • 让一个 -1 乘 -1,则使得所有元素之和增加 2
  • 让 0 乘 -1 无任何改变

记原数组中所有元素之和为 sumsum,则:

  • 如果 sumsum 是 0,无需操作
  • 如果 sumsum 是奇数,无解
  • 如果 sum>0sum>0,我们需要找 k=sum2k=\frac{sum}{2} 个互不相邻的 1,使其乘上 -1
  • 如果 sum<0sum<0,我们需要找 k=sum2k=-\frac{sum}{2} 个互不相邻的 -1,使其乘上 -1

在数组中找 k 个不相邻的 1(或 -1)显然可以贪心求解,取可以取的最靠前的元素即可。由上文,使得一个元素乘上 -1,即使其和前一个元素位于同一个段,按要求输出划分方案。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=500001;
int v[N],a[N],n,m;
int solve()
{
	for (int i=0;i<=n;++i) v[i]=0;
	scanf("%d",&n);
	int sum=0;
	for (int i=1;i<=n;++i) scanf("%d",&a[i]),sum+=a[i];
	if (sum%2) return printf("-1\n"),0;
	int f=0;
	m=n;
	if (sum>0) f=1;
	else f=-1;
	for (int i=2;i<=n&&sum;++i)
	{
		if (a[i]==f&&v[i-1]!=1)
		{
			m--;
			v[i]=1;
			sum-=2*f;
		}
	}
	if (sum!=0) return printf("-1\n"),0;
	printf("%d\n",m);
	for (int l=1,r=1;r<=n;l=r=r+1)
	{
		if (v[r+1]) r++;
		printf("%d %d\n",l,r);
	}
}
int main()
{
	int T=1;
	for (scanf("%d",&T);T--;) solve();
	return 0;
}

D Factorial Divisibility

题目大意

给出 a1,a2,...,ana_1,a_2,...,a_nxx,判断 a1!+a2!+...+an!a_1!+a_2!+...+a_n! 能否整除 x!x!

思路

i!×(i+1)=(i+1)!i!\times (i+1)=(i+1)!,判断所有比 x!x! 小的阶乘是否能全部凑成 x!x! 即可。

代码

#include <bits/stdc++.h>
#define nnn printf("No\n")
#define yyy printf("Yes\n")
using namespace std;
const int N=500001;
int n,m,k;
int v[N];
int solve()
{
	scanf("%d",&n);
	scanf("%d",&k);
	for (int i=1,x;i<=n;++i)
	{
		scanf("%d",&x);
		v[x]++;
	}
	for (int i=1;i<k;++i)
	{
		v[i+1]+=v[i]/(i+1);
		v[i]%=(i+1);
		if (v[i]!=0) return 0;
	}
	return 1;
}
int main()
{
    if (solve()) yyy;
    else nnn;
	return 0;
}

E Wish I Knew How to Sort

题目大意

给一个长度为 nn 的 01 数组 a1,a2,...,ana_1,a_2,...,a_n。每次操作等概率的选择一对下标 1i<jn1\le i<j\le n,如果 ai>aja_i>a_j 就交换它们的值。

求希望执行多少次操作可以使数组不降。

思路

没开 long long 答案错误两发血亏(

统计整个数组中 0 的数量 cnt0cnt0,再统计前 cnt0cnt0 个元素中有多少个 1,记作 cntcnt。容易发现 cntcnt 表示有多少个 0 不在应该在的位置上。

我们定义一次成功的操作需要满足以下条件:

  1. 1icnt01\le i \le cnt0
  2. ai=1a_i=1
  3. cnt0<jncnt0<j\le n
  4. aj=0a_j=0

一次成功的操作可以使 cntcnt 减少 1,不满足上述条件的操作则不改变 cntcnt

共有 n×(n1)2\frac{n\times (n-1)}{2} 个可能被选择的下标对,其中成功的操作有 cnt2cnt^2 个,则一次操作是有效的的概率为 cnt2n×(n1)2\frac{cnt^2}{\frac{n\times (n-1)}{2}}。设 xx 表示从当前状态使 cntcnt 减少 1 的期望操作步数,则:

x=cnt2n×(n1)2×0+n×(n1)2cnt2n×(n1)2×x+1x=\frac{cnt^2}{\frac{n\times (n-1)}{2}}\times 0+\frac{\frac{n\times (n-1)}{2}-cnt^2}{\frac{n\times (n-1)}{2}}\times x+1

即执行了 1 次操作后,有 pp 的概率本次操作为有效操作,还需要进行 0 次即可使 cntcnt 减少 1,有 1p1-p 的概率还需要进行 xx 次才能使 cntcnt 减少 1。

解得:

x=n×(n1)2cnt2x=\frac{\frac{n\times (n-1)}{2}}{cnt^2}

使 cntcnt 变为 0 时,没有 0 不在应该在的位置上,数组单调不降。

xx 循环计算求和即可。

代码

#include <bits/stdc++.h>
#define nnn printf("No\n")
#define yyy printf("Yes\n")
using namespace std;
using LL=long long;
const int N=500001;
const LL mod=998244353;
int n,m,k;
int a[N];
LL inv[N];
LL poww(LL a,LL b)
{
	LL ans=1;
	for (;b;b>>=1,a=a*a%mod)
		if (b&1) ans=ans*a%mod;
	return ans;
}
int solve()
{
	scanf("%d",&n);
	int cnt=0,cnt0=0;
	for (int i=1;i<=n;++i)
	{
		scanf("%d",&a[i]);
		if (a[i]==0) cnt++;
	}
	for (int i=cnt+1;i<=n;++i)
		if (a[i]==0) cnt0++;
	LL ans=0;
	for (int i=cnt0;i>=1;--i)
	{
		ans+=(1ll*n*(n-1)/2)%mod*inv[i]%mod;
		ans%=mod;
	}
	printf("%lld\n",ans);
	return 1;
}
int main()
{
	int T=1;
	inv[0]=1;
	for (int i=1;i<=200000;++i) inv[i]=poww(1ll*i*i,mod-2);
	for (scanf("%d",&T);T--;) solve();
	return 0;
}