Codeforces Round #829-1754A+B与1753A+B+C 题解

1754A - Technical Support

题意

给定一个只包含大写字母 \(\texttt{Q}\)\(\texttt{A}\) 的字符串,如果字符串里的每一个 \(\texttt{Q}\) 都能与在其之后\(\texttt{A}\) 一一对应地匹配,则输出字符串 \(\texttt{Yes}\),否则输出字符串 \(\texttt{No}\)。注意,可以有 \(\texttt{A}\) 没有被匹配,但每个 \(\texttt{Q}\) 必须成功地匹配。

题解

倒着扫一遍,看某一个 \(\texttt{Q}\) 后面的 \(\texttt{A}\) 的数量是否大于 \(\texttt{Q}\) 的数量即可,有一个不满足就说明不可以,全部满足就可以

Code

#include<bits/stdc++.h>
using namespace std;
const int N=505;
int n;
char a[N];
inline void Main()
{
	cin>>n;
	cin>>(a+1);
	int cnt=0;
	for(int i=n;i;--i)
	{
		if(a[i]=='A')++cnt;
		else
		{
			if(cnt>0)--cnt;
			else return puts("No"),void(0);
		}
	}
	puts("Yes");
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr),cout.tie(nullptr);
	int T;
	cin>>T;
	while(T--)Main();
	return 0;
}

1754B - Kevin and Permutation

题意

给定 \(1\)\(n\) 的序列,你需要构造一种排列这 \(n\) 个数的方式使得任意两个相邻的数的差的绝对值的最小值最大,形式化地说,你需要最大化 \(\min_{i=1}^{n-1}|a_i-a_{i+1}|\),请给出任意一种构造方式

题解

打个表不难发现 \(\min_{i=1}^{n-1}|a_i-a_{i+1}|\) 的最大值就是 \(\lfloor\frac n2\rfloor\)

证明也不难,假设最大值是 \(\lfloor\frac n2\rfloor+1\),首先序列中一定会出现这个元素,那这个元素和它相邻的元素的差的绝对值一定至少有一个小于等于 \(\lfloor\frac n2\rfloor\)

\(n\) 是偶数,直接 \(\frac n2,n,\frac n2-1,n-1,\dots,\frac n2+1,1\) 这样排列就可以了

\(n\) 是奇数,把 \(n\) 放在最开始,后面的和偶数一样排列就行,像这样:\(n,\frac{n-1}2,n-1,\frac{n-1}2-1,n-2,\dots,1,\frac{n-1}2+1\)

Code

#include<bits/stdc++.h>
using namespace std;
const int N=1006;
int n,dis;
inline void Main()
{
	cin>>n;
	if(n&1)cout<<n<<' ';
	dis=n>>1;
	for(int i=dis;i;--i) cout<<i<<' '<<i+dis<<' ';
	cout<<'\n';
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr),cout.tie(nullptr);
	int T=1;
	cin>>T;
	while(T--)Main();
	return 0;
}

1753A - Make Nonzero Sum

题意

给定一个长度为 \(n\) 的序列,仅由 \(0,1,-1\) 组成(简单版题目仅由 \(1,-1\) 组成),你需要将这个序列分割为若干段

对于一段 \([l_i,r_i]\) 来说,\(s_i\) 表示这一段中的元素交替加减(第一个元素为加)得到的答案,例如 \(0,1,-1\) 这一段的 \(s_i=+0-(1)+(-1)=-2\)

假设一共有 \(k\) 段,你需要保证 \(\sum_{i=1}^ks_i=0\)

请给出任意一种划分方法,若不存在这样的划分方法,输出 -1

题解

考虑对于某一个元素,不同的划分方法只会改变它对于最终答案的贡献的正负,并不会改变贡献的奇偶性,所以说若所有元素的和为奇数,那么就一定无解

再看序列中的元素仅有 \(1,-1\)

由我们上面得到的结论,可以发现在这种情况下一定一共有偶数个元素,考虑对于相邻的两个元素若为 \(1,1\)\(-1,-1\),这两个分为一组即可,若为 \(1,-1\)\(-1,1\) 就分开,单个元素作为一组,这两个区间的贡献加起来也为 \(0\),这样最后的序列一定满足题意

再看困难版本,也就是序列中的元素有 \(0,1,-1\)

其实与简单版差别并不大,我们还是把相邻的两个不为 \(0\) 的数先分为一组(一定一共有偶数个非零的数字),固定左边界,右边界不断往右扩展,直到找到第二个不为 \(0\) 的数字,现在我们只需要想办法让这个区间的贡献为 \(0\) 即可

先计算这个区间的贡献,若贡献已经为 \(0\) 那就直接使用这个区间,下面考虑贡献不为 \(0\)

这个区间最右边的元素一定非 \(0\),若这个区间长度为偶数,那最后一个元素计算贡献的时候就会取反,把最后一个元素单独拿出来作为一段,这样就一定能和前面的区间的答案抵消,整个区间的贡献就是 \(0\)

若区间长度为奇数,我们再分类一下,若区间的第一个元素非 \(0\),就直接把它单独作为一段,后面部分的贡献就会取反,这样就一定能和前面的区间的答案抵消,整个区间的贡献也是 \(0\)

若区间长度为奇数,同时区间的第一个元素为 \(0\),就把这个 \(0\) 单独作为一段,后面的按照区间长度为偶数的方法处理即可

Code

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,sum,lst1,a[N];
vector<pair<int,int>>ans;
void Main()
{
	cin>>n;
	lst1=sum=0;
	for(int i=1;i<=n;++i)cin>>a[i],sum+=a[i];
	if(sum&1)return cout<<"-1\n",void(0);
	ans.clear();
	for(int i=1,lst=1,f1=-1;i<=n;++i)
	{
		if(a[i]==0)continue;
		lst1=i;
		if(f1==-1){f1=i;continue;}
		if((((f1-lst+1)&1)?a[f1]:-a[f1])+(((i-lst+1)&1)?a[i]:-a[i])==0)
		{
			ans.emplace_back(make_pair(lst,i));
			lst=i+1;
			f1=-1;
			continue;
		}
		if((~(i-lst+1))&1)
		{
			ans.emplace_back(make_pair(lst,i-1));
			ans.emplace_back(make_pair(i,i));
			lst=i+1;
			f1=-1;
			continue;
		}
		if((i-lst+1)&1)
		{
			if(a[lst])
			{
				ans.emplace_back(make_pair(lst,lst));
				ans.emplace_back(make_pair(lst+1,i));
				lst=i+1;
				f1=-1;
				continue;
			}
			else
			{
				ans.emplace_back(make_pair(lst,lst));
				++lst;
				--i;
				continue;
			}
		}
	}
	if(lst1!=n)ans.emplace_back(lst1+1,n);
	cout<<ans.size()<<'\n';
	for(auto x:ans)cout<<x.first<<' '<<x.second<<'\n';
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr),cout.tie(nullptr);
	int T=1;
	cin>>T;
	while(T--)Main();
	return 0;
}

1753B - Factorial Divisibility

题意

给定两个正整数 \(n\)\(x\) 和一个正整数序列 \(a_1,a_2,\dots,a_n\),询问 \(\sum_{i = 1}^n a_i!\) 是否能被 \(x!\) 整除

题解

显然 \(k!\times(k+1)=(k+1)!\),于是乎我们把 \(a_i\) 装进桶里,从小到大枚举,只要能进位就进位,让 \(k+1\) 的阶乘数量加上就好

假设这样进位完毕之后最小的阶乘为 \(k!\),只要 \(k\ge x\) 就说明可以整除,否则不行

Code

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+1;
int n,x,mn;
int a[N];
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr),cout.tie(nullptr);
	cin>>n>>x;
	for(int i=1,tmp;i<=n;++i)cin>>tmp,++a[tmp];
	for(int i=1;i<=N;++i)
	{
		a[i+1]+=a[i]/(i+1);
		a[i]%=(i+1);
		if(a[i])
		{
			mn=i;
			break;
		}
	}
	puts(mn<x?"No":"Yes");
	return 0;
}

1753C - Wish I Knew How to Sort

题意

给出一个 01 序列,每次操作会随机选择两个数交换,求期望要多少次操作才能使整个序列单调不降(答案对于 \(998244353\) 取模)

题解

假设一共有 \(c\) 个 1,显然最终的序列最后 \(c\) 个都是 1,假设现在最后 \(c\) 个数中有 \(k\) 个 1 和 \(c-k\) 个 0,那么前面的 \(n-c\) 个数中就一定有 \(c-k\) 个 1

要希望整个序列单调不降,那就是要把前 \(n-c\) 个数中的 1 和后 \(c\) 个数中的 0 交换,这样的交换一共有 \((c-k)^2\) 种换法,总共的换法为 \(\frac{n(n-1)}2\),所以成功让后 \(c\) 个数中多出一个 1 的概率就是 \(\frac{(c-k)^2}{\frac{n(n-1)}2}=\frac{2(c-k)^2}{n(n-1)}\)

期望操作次数就是 \(\frac{n(n-1)}{2(c-k)^2}\),然后 \(k\) 就会增加 \(1\),直到 \(k\) 增加到 \(c\) 就满足条件了

假设最后 \(c\) 个数中一开始有 \(\text{cnt}_1\) 个 1 最终的答案就是

\[\sum_{k=\text{cnt}_1}^c\frac{n(n-1)}{2(c-k)^2} \]

Code

// LUOGU_RID: 91311868
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5,mod=998244353;
int n,cnt,k,all,ans;
int a[N];
inline void Mul(int &x,int y){ll z=1ll*x*y;if(z>=mod)z%=mod;x=z;}
inline int mul(int x,int y){ll z=1ll*x*y;if(z>=mod)z%=mod;return z;}
inline void Add(int &x,int y){x+=y;if(x>=mod)x-=mod;}
inline int add(int x,int y){x+=y;if(x>=mod)x-=mod;return x;}
inline int fp(int x,int p)
{
	int ans=1;
	for(;p;p>>=1,Mul(x,x))if(p&1)Mul(ans,x);
	return ans;
}
inline int inv(int x){return fp(x,mod-2);}
void Main()
{
	cin>>n;
	all=mul(mul(n,n-1),inv(2));
	k=ans=cnt=0;
	for(int i=1;i<=n;++i)cin>>a[i],cnt+=a[i];
	for(int i=n-cnt+1;i<=n;++i)k+=a[i];
	for(;k<=cnt;++k)Add(ans,mul(all,inv(mul(cnt-k,cnt-k))));
	cout<<ans<<'\n';
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr),cout.tie(nullptr);
	int T=1;
	cin>>T;
	while(T--)Main();
	return 0;
}

博客园传送门

知乎传送门

posted @ 2022-10-25 07:48  人形魔芋  阅读(37)  评论(0编辑  收藏  举报