A. Everything Everywhere All But One
题意:给你一个序列,问是否可以选出其中 \(n-1\) 个数,使其平均值与剩下的那个数相等( \(n\leqslant50\) )
解法:暴力枚举
点击查看代码
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<vector>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=2002000,mod=998244353;
const double eps=1e-8;
int a[WR];
int read(){
int s=0,w=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*w;
}
signed main(){
int T=read();
while(T--){
int n=read(),tot=0;
bool flag=false;
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=n;i++){
tot=0;
for(int j=1;j<=n;j++){
if(j!=i) tot+=a[j];
}
double res=(double)tot/(n-1);
if(fabs(res-(double)a[i])<eps) printf("Yes\n"),flag=true;
if(flag) break;
}
if(!flag) printf("No\n");
}
return 0;
}
B. Odd Subarrays
题意:给你一个 \(1\sim n\) 的排列,可以将其划分成多个子串,使其中逆序对数量为奇数的子串数目最大
输出最大数目,\(n\leqslant 10^5\)
解法:先手模一个排列,比如 1 3 7 5 6 4 2 8
不难发现将其划分为 1 3 | 7 5 | 6 4 | 2 8
时答案最大
大胆猜想,由于在一个平凡的逆序对 \((a_i,a_j)\) 中 \(\tau=1\) (也就是逆序对数为 \(1\) )满足条件
因此我们可以遇见一个逆序对就划分,这样肯定更优秀
发现他过了,考虑如何证明
比如对于一个序列 \(a_1,a_2,a_3,a_4~(a_3>a_4>a_1>a_2)\) 如果不断开 \(\tau=2\)
如果断开的在 \(a_3\) 后面则答案要 \(+1\) ,比起在 \(a_2\) 处断开 \(ans+2\) 更劣
其余情况不难依此得出
点击查看代码
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<vector>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=2002000,mod=998244353;
int a[WR];
int read(){
int s=0,w=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*w;
}
signed main(){
int T=read();
while(T--){
int n=read();
int ans=0;
for(int i=1;i<=n;i++){
a[i]=read();
if(a[i]<a[i-1]){
ans++,i++;
if(i<=n) a[i]=read();
}
}
printf("%lld\n",ans);
}
return 0;
}
C. Circular Local MiniMax
题意:给你一个数组 \(a\) ,问可否将其重新排列成一个环 \(b\) 使所有的 \(b_i\) 均满足
\[b_{i-1}>b_i<b_{i+1}~\operatorname{or}~b_{i-1}<b_i>b_{i+1}~(2\leqslant i\leqslant n) \]此处令 \(b_{n+1}=b_1\),\(n\leqslant 10^5\)
解法:首先容易注意到如果 \(n\) 是奇数显然无解
然后把数组排一下序,将 \(a_i\) 和 \(a_{i+\frac{n}{2}}\) 放一起
这是因为这样可以最大限度地保证不会安排上两个相同的(下标最小差值更大)
然后扫一遍 \(b\) 数组看是否符合即可
点击查看代码
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<vector>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=2002000,mod=998244353;
int a[WR],rec[WR];
int read(){
int s=0,w=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*w;
}
signed main(){
int T=read();
while(T--){
int n=read();
for(int i=1;i<=n;i++) a[i]=read();
if(n&1) printf("No\n");
else{
sort(a+1,a+1+n);
int tot=0;
for(int i=1;i<=n;i+=2) rec[i]=a[++tot];
for(int i=2;i<=n;i+=2) rec[i]=a[++tot];
bool flag=true;
rec[n+1]=rec[1];
for(int i=2;i<=n;i++){
if(rec[i]>rec[i-1]&&rec[i]>rec[i+1]) continue;
if(rec[i]<rec[i-1]&&rec[i]<rec[i+1]) continue;
flag=false;break;
}
if(flag){
printf("Yes\n");
for(int i=1;i<=n;i++) printf("%lld ",rec[i]);
}else printf("No");
printf("\n");
}
}
return 0;
}
D. Linguistics
首先,观察几个字符串
可以发现:若存在形似 \(\mathrm{ABBAABAB}\) 等有两个相同的字符挨在一起的情况,则我们在它们中间放一块隔板
那么,位于同一区间的所有字符都是相间的形式
也就是说,可以用碎片 \(\rm AB\) 和 \(\rm BA\) 来拼凑出它们
当某一区间的字符个数为奇数时,如 \(\rm ABA\)
若想使用 \(\rm AB\) 或 \(\rm BA\) ,则一定会删去一个 \(\rm A\),而具体是哪一个我们并不关心
但删去不同的 \(\rm A\) 则会导致用于表示这一区间的碎片不同
当某一区间的字符个数为偶数时,如 \(\rm ABABABAB\) ,那么最理想的情况是用四个 \(\rm AB\) 来凑这段区间
而当 \(\rm AB\) 不够用的时候,则可以将原区间用 一些 \(\rm AB+A+\) 一些 \(\rm BA+B\)拼凑出来
也就是说,当前区间为偶数时,可以通过使用一个 \(\rm A\) 与 \(\rm B\) 来使得区间使用的碎片发生变化
综上,可以发现,当区间为奇数时,它使用的碎片是不确定的
而当区间为偶数时,它使用的碎片是确定的(偶区间使用的碎片可以使用 \(\rm A\) 与 \(\rm B\) 来改变)
所以,我们先处理区间为偶数时的情况,因为这时使用的碎片确定
当有多个偶数区间使用的碎片都相同时,我们将这些区间按照大小从小到大排序,这是因为不一定能都填满所有的区间
所以我们从长度小的区间开始填,这样,我们就能保证被填满的区间最多,而没有填满的区间使用 \(\rm A\) 和 \(\rm B\) 来进行转换
这样就能确保我们使用的 \(\rm A\) 和 \(\rm B\) 尽可能少
在我们解决完偶区间后,我们考虑奇区间
首先,若当前区间为 \(\rm ABABABABA\) 的形式,则用一个碎片替代最前面或最后面的(相当于删去最前面或最后面的)
这样就能使用 \(\rm AB\) 或 \(\rm BA\) 去填满这个区间
而若我们剩下的或不能靠自己这一类去填满当前区间,也就是我们需要用两种碎片来拼凑同一区间
则我们将碎片替代的字符变成区间中一个非端点字符,此时区间被划分成了两半
左边一半使用的碎片为一种,右边一半使用的碎片为另外一种
综上,对于奇区间,无论怎样用 \(\rm AB\) 和 \(\rm BA\) 拼凑,都只需要用一个 \(\rm A\) 或 \(\rm B\)
所以我们只需要在偶区间操作完后再判断一下 \(\rm A/B\) 的数量 \(+\rm AB\) 的数量 \(+\rm BA\) 的数量是否能填满奇区间即可
对于 \(\rm A/B\):若我们先将奇区间中使用的单个A和B删去后,A和B的数量一定是相等的
因为它们在偶区间中的使用是成对出现的
由于代码意识流,解说转自 https://www.cnblogs.com/LuoyuSitfitw/p/16624323.html
点击查看代码
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<iostream>
#include<vector>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=2002000,mod=998244353;
char str[WR];
bool vis[WR];
int read(){
int s=0,w=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*w;
}
signed main(){
int T=read();
while(T--){
int a=read(),b=read(),c=read(),d=read();
scanf("%s",str+1);
int len=strlen(str+1);
int cntA=0,cntB=0;
for(int i=1;i<=len;i++){
if(str[i]=='A') cntA++;
else cntB++;
}
if(cntA!=a+c+d||cntB!=b+c+d){
printf("No\n");continue;
}
int cntAB=0,cntBA=0;
vector<int>ab,ba;
for(int l=1;l<=len;l++){
int r=l;
while(r<len&&str[r+1]!=str[r]) r++;
if((r-l+1)&1){
if(str[l]=='A') a--;
else b--;
}else{
if(str[l]=='A') ab.emplace_back((r-l+1)>>1),cntAB+=((r-l+1)>>1);
else ba.emplace_back((r-l+1)>>1),cntBA+=((r-l+1)>>1);
}
l=r;
}
if(a<0||b<0){
printf("No\n");
continue;
}
sort(ab.begin(),ab.end());
int tmp=min(a,b);
while(!ab.empty()&&tmp){
tmp--;
cntAB-=ab.back();
ab.pop_back();
}
if(cntAB>c){
printf("No\n");
continue;
}
sort(ba.begin(),ba.end());
tmp=min(a,b);
while(!ba.empty()&&tmp){
tmp--;
cntBA-=ba.back();
ba.pop_back();
}
if(cntBA>d){
printf("No\n");
continue;
}
printf("Yes\n");
}
return 0;
}