CF7 合集

前言:

  1. 本人不会 LaTeX……请见谅
  2. 码风奇特,不喜勿喷哈
  3. 题面翻译取自 luogu,本蒟蒻也会安置原题链接
  4. 保证文章中不出现“显然”或者“注意到”,可能会出现“易证”
  5. AC 代码会放置在每一个题目的最底端,为防止 ban 码的情况出现,不设置跳转链接
  6. 有写错的地方欢迎各位神犇指正
  7. 本套题共 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,繁化简,多变少

  • 先不考虑完整的运算法则……也不考虑完整的表达式
  1. 钦定只有“+”“-”两种运算(不含括号)
外层符号 宏定义符号 不打括号结果 打括号结果
+ + 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

观察发现,当内层为 “+”“-” 时前面只有为 “-” 才会不合法,那么合法情况就是前面为 “+”

  1. 钦定只有“×”“÷”两种运算(不含括号)
外层符号 宏定义符号 不打括号结果 打括号结果
× × 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

同理,当内层为 “×”“÷” 时前面只有为 “÷” 才会不合法,那么合法情况就是前面为 “×”

  1. 钦定同时存在四则运算(不含括号)
外层符号 宏定义符号 不打括号结果 打括号结果
+ + 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 种情况不合法~

  1. 处理括号

经典 trick:递归处理,把括号内运算的结果当做变量……

Step 4,代码可读性优化

  • 历经九九八十一难,我们终于还原了题意……
  • 考虑到 12 种情况实在是有点废人……
  • 观察到宏定义里的符号都是 (+,−)/(×,÷) 两两为一组出现的,所以我们可以只记录是否出现 +/− 以及是否出现 ×/÷
  • 进一步,我们将所有宏定义分为四大类
  1. 自身是合法的,出现了 +−
  2. 自身是合法的,没出现 +−,出现了 ×÷
  3. 自身是合法的,什么都没出现(就代表一个变量)
  4. 自身是不合法的
  • 嗯哼,接近尾声了呢!

Step 5,状态检验

  • 某个状态实际上如果一步一步这么推过来是比较自然的,但是防止我们漏掉某些情况或者某些转移有误,我们引入状态机进行检验
  1. 每个状态遇到所有可能碰到的条件都必须有出路,否则这个状态碰到该条件时就会无法转移
  2. 每个状态与每个条件都只能有一个转移路径,这个也是好理解的,否则一个状态它就不知道转移到哪个状态了
  3. 状态机要包含所有状态
  • 那么验证一下定义的状态机
  • 第三点是显然的,每个宏定义只有:合法/不合法两种状态,而不合法状态又分为:有符号/无符号两种,有符号又分为 +− 和 ×÷ 两种
  • 第二点根据我们的转移也是成立的,每种宏定义状态与每个外部符号结合后的转移都是唯一的(注意这里外部符号分为前后两种)
  • 第一点在状态转移中虽然没有提及,但是也是隐藏的,就是如果合法(不合法 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;
}

完结撒花!

posted @ 2024-11-11 14:32  sunxuhetai  阅读(151)  评论(0)    收藏  举报