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 最终的答案就是
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;
}