CF7 合集
前言:
- 本人不会 LaTeX……请见谅
- 码风奇特,不喜勿喷哈
- 题面翻译取自 luogu,本蒟蒻也会安置原题链接
- 保证文章中不出现“显然”或者“注意到”,可能会出现“易证”
- AC 代码会放置在每一个题目的最底端,为防止 ban 码的情况出现,不设置跳转链接
- 有写错的地方欢迎各位神犇指正
- 本套题共 5 道,前四题属于超级水题(板子题……),预计阅读 + 理解时间 20min,最后一题涉及大量的分类讨论,预计阅读 + 理解时间不小于 40min
正片开始!
CF7A
题面(可从下方链接跳转看原题题面):
原棋盘为8行8列的白色棋盘,每次可将一行或一列染成黑色,问至少需要多少次才能将棋盘染成样例所示棋盘
输入:目标棋盘状态,W表示白,B表示黑
输出:最少操作次数
序言 & 结论:
难度:橙题
吐槽:因忘记特判而挂分
推理过程:
- 行、列分别扫描
- 有黑色的块就 flag=true
- 扫描后若 flag 被更新,则答案自增 1
细节处理:
- 直接做,完了
- 考虑一种情况,形如:
BBBBBBBB
BBBBBBBB
BBBBBBBB
BBBBBBBB
BBBBBBBB
BBBBBBBB
BBBBBBBB
BBBBBBBB
- 此情况需要特判!
代码:
雨——联想了云
璇——联想了玑
可怜一段往事,再无终章
如天穹凝落
故名:云落
点击查看代码
#include<iostream>
using namespace std;
char a[8][8];
int main(){
for(int i=0;i<8;i++){
for(int j=0;j<8;j++){
cin>>a[i][j];
}
}
int ans=0;
for(int i=0;i<8;i++){
bool flag=true;
for(int j=0;j<8;j++){
if(a[i][j]!='B'){
flag=false;
}
}
if(flag){
ans++;
}
}
for(int i=0;i<8;i++){
bool flag=true;
for(int j=0;j<8;j++){
if(a[j][i]!='B'){
flag=false;
}
}
if(flag){
ans++;
}
}
if(ans==16){
ans=8;
}
cout<<ans<<endl;
return 0;
}
----------------------------云落的分割线----------------------------
CF7B
题面(可从下方链接跳转看原题题面):

序言 & 结论:
本以为数据结构
一看,超级魔改版链表?
二看,绿题?
三看,m≤100
四看,水题,O(tm2) 都可过
……(一只乌鸦飞过)
推理过程:
- 记录一个索引
- 直接模拟……
细节处理:
- 建议三个操作分别封装进函数中
- NULL 与 ILLEGAL_ERASE_ARGUMENT 的情况不要忘记判断
代码:
咕了(bushi)
点击查看代码
#include<iostream>
using namespace std;
const int maxm=128;
int idx[maxm],t,m,cnt;
inline void alloc(int x){
int l=-1,r=0;
while(r<m){
if(r-l==x&&idx[r]==0){
break;
}
if(idx[r]){
l=r;
}
r++;
}
if(r-l==x&&r<m){
cnt++;
cout<<cnt<<endl;
for(int i=l+1;i<=r;i++){
idx[i]=cnt;
}
}else{
cout<<"NULL"<<endl;
}
return;
}
inline void del(int x){
bool flag=false;
for(int i=0;i<m;i++){
if(idx[i]==x&&x!=0){
idx[i]=0;
flag=true;
}
}
if(!flag){
cout<<"ILLEGAL_ERASE_ARGUMENT"<<endl;
}
return;
}
inline void change(){
int l=0,r=0;
while(r<m){
if(idx[r]){
swap(idx[r],idx[l]);
l++;
}
r++;
}
return;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>t>>m;
while(t--){
string s;
cin>>s;
if(s=="alloc"){
int x;
cin>>x;
alloc(x);
}
if(s=="erase"){
int x;
cin>>x;
del(x);
}
if(s=="defragment"){
change();
}
}
return 0;
}
----------------------------云落的分割线----------------------------
CF7C
题面(可从下方链接跳转看原题题面):
一条直线:Ax+By+C=0(AB不同时为0),找到任意一个点(在[-5e18,5e18]之间)让它的横纵坐标均为整数,或者确定没有这样的点。
输入:A,B,C
输出:该点坐标,没有就输出 -1
序言 & 结论:
难度:绿题(会板子的话,应该是个红?)
推理过程:
- 两句话秒了(算上这句)
- exgcd
细节处理:

代码:
贴个代码(寻思着其实代码都可以不用贴)
不开祖宗见 long long 哈!
点击查看代码
#include<iostream>
#include<cstdio>
#define int long long
using namespace std;
int a,b,c,x,y;
inline int exgcd(int a,int b){
if(b==0){
x=1;
y=0;
return a;
}
int res=exgcd(b,a%b);
int tmp=x;
x=y;
y=tmp-a/b*y;
return res;
}
signed main(){
scanf("%lld%lld%lld",&a,&b,&c);
c=-c;
int d=exgcd(a,b);
if(c%d!=0){
printf("-1\n");
return 0;
}
x=c/d*x;
y=c/d*y;
printf("%lld %lld\n",x,y);
return 0;
}
----------------------------云落的分割线----------------------------
CF7D
题面(可从下方链接跳转看原题题面):

序言 & 结论:
字符串!回文!
难度:绿题(这套题绿题好多)
提供两种做法 hash 和 Manacher(知道你们爱看代码,都贴了)
推理过程:
- 钦定 dp[i] 表示 [1,i] 串的阶级
- 由题意,显然有
非回文串:dp[i]=0
回文串:dp[i]=dp[i/2]+1 - 在 dp 数组解决计算问题后,就是一个判断回文的裸题咯!
- 可以使用 hash 或 Manacher
细节处理:
- Manacher 双倍空间!
- base 选取建议使用大质数(考虑到字符串长度 5e6)
- 千万不要开 long long,不然你会 AC
代码:
hash 代码:
点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#define int long long
using namespace std;
const int maxn=5e6+10,base=9973;
char s1[maxn],s2[maxn];
int hash1[maxn],hash2[maxn];
int dp[maxn];
int tot=0;
int p[maxn];
inline int get1(int l,int r){
return hash1[r]-hash1[l-1]*p[r-l+1];
}
inline int get2(int l,int r){
return hash2[r]-hash2[l-1]*p[r-l+1];
}
signed main(){
scanf("%s1",s1+1);
int n=strlen(s1+1);
for(int i=1;i<=n;i++){
s2[i]=s1[n-i+1];
}
p[0]=1;
for(int i=1;i<=n;i++){
p[i]=p[i-1]*base;
}
for(int i=1;i<=n;i++){
hash1[i]=hash1[i-1]*base+(int)(s1[i]);
}
for(int i=1;i<=n;i++){
hash2[i]=hash2[i-1]*base+(int)(s2[i]);
}
int ans=0;
for(int i=1;i<=n;i++){
int mid=i>>1;
int pos1=get1(1,mid),pos2=get2(n-i+1,n-i+mid);
if(pos1==pos2){
dp[i]=dp[mid]+1;
}
ans+=dp[i];
}
printf("%lld\n",ans);
return 0;
}
Manacher 代码:
点击查看代码
#include<iostream>
#include<cstring>
#define int long long
using namespace std;
typedef long long ll;
const int maxn=5e6+10;
char s[maxn<<1];
int p[maxn<<1];
int n,len;
int dp[maxn];
inline int Manacher(){
int mid=0,mx=1,ans=0;
p[0]=p[1]=1;
for(int i=0;i<n;i++){
p[i]=1;
if(i<mx){
p[i]=min(mx-i,p[(mid<<1)-i]);
}
int k=i+p[i];
while(s[i-p[i]]==s[i+p[i]]){
k++;
p[i]++;
}
if(mx<i+p[i]){
mid=i;
mx=i+p[i];
}
if(2*i-k==0&&i>1){
int l=p[i]-1;
dp[l]=dp[l/2]+1;
ans+=dp[l];
}
}
return ans;
}
inline void init(){
s[0]='$';
s[n]='#';
s[n+1]='\0';
for(int i=len*2;i>=1;--i){
if(i&1){
s[i]='#';
}else{
s[i]=s[i/2];
}
}
return;
}
signed main(){
scanf("%s",s+1);
len=strlen(s+1);
n=2*len+1;
init();
int ans=Manacher();
printf("%lld\n",ans);
return 0;
}
另附:AC 记录

----------------------------云落的分割线----------------------------
CF7E
题面(可从下方链接跳转看原题题面):
题目给了一系列 C++ 的宏定义,问你一个表达式是否是“安全"的。安全的定义是,展开后的表达式中,所有的宏在计算过程中都被作为一个整体运算。
如 #define sum x+y 后,2sum 就会被替换成 2x+y 此时此时因为乘号优先级比加号高,导致宏在实际计算中被拆开了,可能产生错误。
宏的个数 ≤100,每个表达式长度 ≤100,只有四则运算和括号
序言 & 结论:
温馨提示:鉴于代码没有可读性,请仔细阅读“解题思路”部分
难度:黑题降蓝题(本蒟蒻觉得就是个中位蓝)
疯狂的分类讨论——好活当赏!
推理过程:
Step 1,解决输入的冗余部分(真就万事开头难?)
- 由前人留下的“警示后人”,本蒟蒻得知,数据中存在一些毒瘤输入,形如:
# define s+y - “大 量 的 多 余 的 空 格”
- 直接什么字符数组肯定是不行的,getline(cin,s) 才是王道
Step 2,题意略做转化
- 判断表达式最后安不安全实在是过分抽象
- 考虑把最后的表达式看作一个宏定义,只需要实现判断宏定义是否合法即可,令问题统一化
Step 3,繁化简,多变少
- 先不考虑完整的运算法则……也不考虑完整的表达式
- 钦定只有“+”“-”两种运算(不含括号)
| 外层符号 | 宏定义符号 | 不打括号结果 | 打括号结果 |
|---|---|---|---|
| + | + | a+b+c | a+(b+c)=a+b+c |
| + | - | a+b-c | a+(b-c)=a+b-c |
| - | + | a-b+c | a-(b+c)=a-b-c |
| - | - | a-b-c | a-(b-c)=a-b+c |
观察发现,当内层为 “+”“-” 时前面只有为 “-” 才会不合法,那么合法情况就是前面为 “+”
- 钦定只有“×”“÷”两种运算(不含括号)
| 外层符号 | 宏定义符号 | 不打括号结果 | 打括号结果 |
|---|---|---|---|
| × | × | a×b×c | a×(b×c)=a×b×c |
| × | ÷ | a×b÷c | a×(b÷c)=a×b÷c |
| ÷ | × | a÷b×c | a÷(b×c)=a÷b÷c |
| ÷ | ÷ | a÷b÷c | a÷(b÷c)=a÷b×c |
同理,当内层为 “×”“÷” 时前面只有为 “÷” 才会不合法,那么合法情况就是前面为 “×”
- 钦定同时存在四则运算(不含括号)
| 外层符号 | 宏定义符号 | 不打括号结果 | 打括号结果 |
|---|---|---|---|
| + | + | a+b+c | a+(b+c)=a+b+c |
| + | - | a+b-c | a+(b-c)=a+b-c |
| + | × | a+b×c | a+(b×c)=a+b×c |
| + | ÷ | a+b÷c | a+(b÷c)=a+b÷c |
| - | + | a-b+c | a-(b+c)=a-b-c |
| - | - | a-b-c | a-(b-c)=a-b+c |
| - | × | a-b×c | a-(b×c)=a-b×c |
| - | ÷ | a-b÷c | a-(b÷c)=a-b÷c |
| × | + | a×b+c | a×(b+c)=a×b+a×c |
| × | - | a×b-c | a×(b-c)=a×b-a×c |
| × | × | a×b×c | a×(b×c)=a×b×c |
| × | ÷ | a×b÷c | a×(b÷c)=a×b÷c |
| ÷ | + | a÷b+c | a÷(b+c) |
| ÷ | - | a÷b-c | a÷(b-c) |
| ÷ | × | a÷b×c | a÷(b×c)=a÷b÷c |
| ÷ | ÷ | a÷b÷c | a÷(b÷c)=a÷b×c |
共计 12 种情况不合法~
- 处理括号
经典 trick:递归处理,把括号内运算的结果当做变量……
Step 4,代码可读性优化
- 历经九九八十一难,我们终于还原了题意……
- 考虑到 12 种情况实在是有点废人……
- 观察到宏定义里的符号都是 (+,−)/(×,÷) 两两为一组出现的,所以我们可以只记录是否出现 +/− 以及是否出现 ×/÷
- 进一步,我们将所有宏定义分为四大类
- 自身是合法的,出现了 +−
- 自身是合法的,没出现 +−,出现了 ×÷
- 自身是合法的,什么都没出现(就代表一个变量)
- 自身是不合法的
- 嗯哼,接近尾声了呢!
Step 5,状态检验
- 某个状态实际上如果一步一步这么推过来是比较自然的,但是防止我们漏掉某些情况或者某些转移有误,我们引入状态机进行检验
- 每个状态遇到所有可能碰到的条件都必须有出路,否则这个状态碰到该条件时就会无法转移
- 每个状态与每个条件都只能有一个转移路径,这个也是好理解的,否则一个状态它就不知道转移到哪个状态了
- 状态机要包含所有状态
- 那么验证一下定义的状态机
- 第三点是
显然的,每个宏定义只有:合法/不合法两种状态,而不合法状态又分为:有符号/无符号两种,有符号又分为 +− 和 ×÷ 两种 - 第二点根据我们的转移也是成立的,每种宏定义状态与每个外部符号结合后的转移都是唯一的(注意这里外部符号分为前后两种)
- 第一点在状态转移中虽然没有提及,但是也是隐藏的,就是如果合法(不合法 12 种情况),状态就转移到对应的 1,2,3 号状态,这也就是为什么要把 1,2 状态分离,这样才可以保证每种状态的转移路径唯一
细节处理:
- 这个因人而异吧……
- 本蒟蒻一遍切的,耗时 2h38min
代码:
备花备花!
点击查看代码
#include<iostream>
#include<cstring>
#include<vector>
#include<map>
using namespace std;
int n;
map<string,string> mp;
map<string,int> rk;
inline bool check(char c){
return c=='+'||c=='-'||c=='*'||c=='/';
}
inline int get_rk(char c){
if(c=='+')return 1;
if(c=='-')return 2;
if(c=='*')return 3;
if(c=='/')return 4;
return 0;
}
inline int judge(string s){
if(rk.count(s)){
return rk[s];
}
vector<int> res;
for(int i=0;i<s.length();i++){
if(s[i]=='('){
int cnt=1,j=i;
string str;
while(cnt){
j++;
if(s[j]==')'){
cnt--;
if(cnt==0){
i=j;
if(judge(str)==-1){
return -1;
}
break;
}
}
str+=s[j];
if(s[j]=='('){
cnt++;
}
}
}else{
if(check(s[i])){
res.push_back(get_rk(s[i]));
}else{
string now;
while(i<s.length()&&(isalpha(s[i])||isdigit(s[i]))){
now+=s[i++];
}
i--;
int cur;
if(rk.count(now)){
cur=rk[now];
}else{
cur=0;
}
if(cur==-1){
return -1;
}
if(cur){
if(cur==1){
if(!res.empty()&&res.back()!=1){
return -1;
}
for(int j=i+1;j<s.length();j++){
if(check(s[j])){
if(get_rk(s[j])>2){
return -1;
}
break;
}
}
}else{
if(!res.empty()&&res.back()==4){
return -1;
}
}
}
}
}
}
if(res.empty()){
return 0;
}
for(int i=0;i<res.size();i++){
if(res[i]<=2){
return 1;
}
}
return 2;
}
signed main(){
cin>>n;
if(n==0){
cout<<"OK"<<endl;
return 0;
}
for(int i=1;i<=n;++i){
string str;
cin>>str;
if(str=="#"){
cin>>str;
}
cin>>str;
string t1;
getline(cin,t1);
string s1;
for(int i=0;i<t1.length();i++){
if(t1[i]!=' '){
s1+=t1[i];
}
}
mp[str]=s1;
int iu=judge(s1);
rk[str]=iu;
}
string t2;
getline(cin,t2);
string s2;
for(int i=0;i<t2.length();i++){
if(t2[i]!=' '){
s2+=t2[i];
}
}
if(judge(s2)==-1){
cout<<"Suspicious"<<endl;
}else{
cout<<"OK"<<endl;
}
return 0;
}
完结撒花!

浙公网安备 33010602011771号