Educational Codeforces Round 163
A - Special Characters
签到题,循环输出AABB即可(有人输出AAB,我觉得也行)
void solve(){
CIN(n);
if(n%2==0){
CY;
FOR(i,1,n/2){//感觉还是AAB更优雅一点
cout<<"AB"[i%2]<<"AB"[i%2];
}
cout<<ET;
}else{
CN;
}
}
B - Array Fix
贪心题,倒序遍历一遍,如果找到首次不合法则特判这个不合法能不能通过拆分解决,如果不能直接返回,如果能,后面继续遍历时所有数都得拆。
void solve(){
CIN(n);
vector<ll> a;a.push_back(0);
FOR(i,1,n){
CIN(now);a.push_back(now);
}
ll MIN_MAX=a[n];
bool flag=0;
rFOR(i,n,2){
if(!flag){
if(a[i]<a[i-1]){
if(a[i-1]<10){CN;return;}
if(a[i-1]%10<a[i-1]/10){CN;return;}
if(a[i-1]%10>a[i]){CN;return;}
flag=1;
}
}else{
if(a[i]%10<a[i]/10){CN;return;}
if(a[i-1]%10<a[i-1]/10){CN;return;}
if(a[i]>=10){
if(a[i-1]%10>a[i]/10){CN;return;}
}else{
if(a[i-1]%10>a[i]){CN;return;}
}
}
}
CY;return;
}
C - Arrow Path
数据量略有点小,直接无脑上BFS即可,推导一下也行,但是脑子不够快的话就不要把时间花在推式子上了。$\text{\color{#ff0000}{写BFS和DFS记得剪枝!!!!}}$
bool vis[3][200005];
void solve(){
CIN(n);
string a[3];
cin>>a[1]>>a[2];
a[1]=" "+a[1];
a[2]=" "+a[2];
map<char,int> move;
move['<']=-1;move['>']=1;
memset(vis,0,sizeof vis);
queue<pair<int,int>> q;
q.push({1,1});
while(!q.empty()){
auto &[y,x]=q.front();q.pop();
if((y==2&&x==n)||(y==1&&x==n)||(y==2&&x==n-1)){CY;return;}
if(vis[y][x])continue;
vis[y][x]=1;
if(x>=3&&move[a[y][x-1]]!=1&&!vis[y][x-2])q.push({y,x-2});
if(x<=n-2&&move[a[y][x+1]]!=-1&&!vis[y][x+2])q.push({y,x+2});
if(y==1&&!vis[2][x+move[a[2][x]]])q.push({2,x+move[a[2][x]]});
if(y==2&&!vis[1][x+move[a[1][x]]])q.push({1,x+move[a[1][x]]});
}
CN;return;
}
D - Tandem Repeats?
暴力即可,但是记得别暴力过头了,$n=3000$ 的数据不是给你开 $O(n^3)$ 的算法用的。记得优化一下。
void solve(){
string s;cin>>s;s=" "+s;
int n=s.length()-1;
int ans=0;
rFOR(offset,n/2,1){
int tmp=0;
FOR(i,1,n-offset+1){//利用tmp++,并在不符合时重新计数来记忆当前遍历的最大连续长度
if(tmp>=offset){cout<<offset*2<<ET;return;}
if(!(s[i+offset]=='?')&&!(s[i]=='?')&&(s[i]!=s[i+offset]))tmp=0;
else tmp++;
}
if(tmp>=offset){cout<<offset*2<<ET;return;}
}
cout<<0<<ET;
}
E - Clique Partition
RF神说是道水题
- “我搞不懂为什么这题的n和k这么小,它完全可以再大一点的”
- “我觉得D是大于E的,E唯一的难点就在于它是E,你看不到它”
题意是给定 $n$ 和 $k$ ,要求构造一个排列,使完全联通块数量尽可能少,也就是要求尽可能连边
分析核心式子 $|i - j| + |a_i - a_j| \le k$,考虑 $1$ 到 $k+1$, $k+1$ 和 $1$ 已经在下标上相差 $k$ 了,再加上数值上的相差,必然不可能连边,所以每个联通块最大为 $k$。因此分组关系的答案应该是 $k$ 个 $1$,$k$ 个 $2$,…,$k$ 个 $n$,$k’$ 个 $n+1$ 的形式(其中 $k’\le k$)
再来考虑如何构造每个组,对于一个组,他的序号是没法改的,所以要使每个组的成员均两两满足式子,先考查 $|i-j|$ 的规律,取极限情况,首项和尾项,得到差值为 $k-1$,然后一步步往中间跳,差值会逐渐缩小。因此 $a_i$ 的选取应当满足:取首项和尾项时相差最小,往中间一步步跳的时候,差值逐渐增大。
所以每个组(偶数情况),构造:$\frac{n}{2},\frac{n}{2}-1,\frac{n}{2}-2,\dots,1,n,n-1,\dots,\frac{n}{2}+1$ 即可。
当然你反过来构造:$\frac{n}{2},\frac{n}{2}+1,\frac{n}{2}+2,\dots,n,1,2,\dots,\frac{n}{2}-1$ 也行。
考虑奇数情况,易知如上构造也符合题意,让正中间的元素最大即可。
void solve(){
CIN(n);CIN(k);
vector<int> a(n+1),GROUP(n+1);
int cnt=0;
auto f=[&](auto f,int l,int gruop){
if(l>n)return;
cnt=gruop;
int num=min(n-l+1,k);//判断剩下的能不能凑一个长度为k的组
int now=l;
//rFOR(j,l+num/2-1,l){a[j]=now++;GROUP[j]=i;}//构造方案1
//rFOR(j,l+num-1,l+num/2){a[j]=now++;GROUP[j]=i;}
FOR(j,l+num/2,l+num-1){a[j]=now++;GROUP[j]=gruop;}//构造方案2
FOR(j,l,l+num/2-1){a[j]=now++;GROUP[j]=gruop;}
f(f,l+num,gruop+1);
};
f(f,1,1);
FOR(i,1,n)cout<<a[i]<<" ";
cout<<ET<<cnt<<ET;
FOR(i,1,n)cout<<GROUP[i]<<" ";
cout<<ET;
}
附录
这里贴上头部定义和main函数部分:
#include<iostream>
#include<vector>
#include<bits\stdc++.h>
#define ll long long
#define ld long double
using namespace std;
#define ET '\n'
#define FOR(i,a,b) for(ll i=(a);i<=(b);i++)
#define rFOR(i,a,b) for(ll i=(a);i>=(b);i--)
#define CIN(a) ll a;cin>>a
#define DCIN(a) ld a;cin>>a
#define CA cout<<ans<<ET
#define CY cout<<"YES"<<ET
#define CN cout<<"NO"<<ET
//没有C++20就不能用ranges了
//#define PP(l,r,CK) *ranges::partition_point(ranges::iota_view((l),(r)+1),(CK))
string ANS[2]={"No\n","Yes\n"};
//solve函数代码
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
//pre();
CIN(t);while(t--)
solve();
return 0;
}