着实好久没打cf,也没写cf的题解了,上菜~
A.Number Transformation 基本题意:给定两个整数 a 和 b ,问你能不能找到一组正整数 x 和 y,使得 a*(y^x) = b
问题分析:这个题虽然要求是 a 与 x 个 y 连乘等于 b,但只要随便找一组符合的 x 和 y 就行,所以直接把 x 等价成 1,问题就变成了是否存在 y 使得:a*y = b,也就是判断 b是否为 a 的倍数
AC代码:#includeusing namespace std; int t, n, a, b; void solve() { if(b % a != 0){ cout <<0 <<" " <<0 < >t; while(t--) { cin >>a >>b; solve(); } return 0; }
B.Dictionary 基本题意:
等价于没有题意~
问题分析:就是简单的两位字母 a~z 排序(ab ac ad ... ba bc ... zx zy),顺便附加条件:两位字母不能相同
这复杂度想暴力暴力,不想暴力就直接列算式算呗,计算过程应该不需要解释吧
AC代码:#includeusing namespace std; int t, n, a, b; string s; void solve() { int res = (s[0]-'a') * 25 + (s[0] >t; while(t--) { cin >>s; solve(); } return 0; }
C.Infinite Replacement 基本题意:
给定字符串 s 和 t ,其中, s 串全部由字母 a 构成,现在可进行下列操作不限次(含 0 次):用 t 串来代替 s 串中的任意一个 a
现在问你利用上述操作可以得到几种不同状态的 s 串
问题分析:其实这个题更像一个分类讨论题,既然 s 串已经是全部由 a 构成的串了,那突破点肯定在 t 串,所以对 t 串分类讨论:
1.含有 a:这样执行上述操作会发生什么呢?s 中的一个 a 变成了另一个长度更长的且仍然含有字母 a 的串。换句话说,执行上面的操作是无法消除 a 的,这样就使得 s 串可以被无限迭代,s 串的种类也就是无数个
2.不含有 a:这样执行一次上述操作,s 串中就会少一个 a ,所以结果数量是可控的,具体是多少呢?s 串中每个 a 所在的位置,都有替换和不替换两种选择,所以不同的字符串数量也就是2 ^ (s串的长度)
3.长度为 1 且为 a:这个就比较简单了,替换不替换都是那个 a ,结果为 1
值得注意的是 s 串的最大长度为 50,2 的 50 次方会超 int,最后结果需要用 long long 输出
AC代码:#includeusing namespace std; int T, n, a, b; string s, t; void solve() { //长度为1且为a if(t.length() == 1 && t[0] == 'a'){ cout <<1 < >T; while(T--) { cin >>s >>t; solve(); } return 0; }
D. A-B-C Sort 基本题意:
t 组数据,每组数据给定一个长度为 n 的数字序列,进行下面两步操作:
( 原序列为a,步骤 1 生成的序列为 b ,步骤 2 生成的序列为 c )
步骤 1:每次拿出序列 a 的最后一个元素 temp ,放到序列 b 的中央,如果序列 b 当前元素的个数为奇数,则可以选择将 temp 放到中央元素的左边或者右边,直到 a 序列为空
步骤 2:拿出序列 b 中最中央的元素(如果中央元素有两个就可以随便选一个),放到序列 c 的末尾,直到序列 b 为空
现在操作完了,问:序列 c 是否能够是升序的
问题分析:这个题有点意思,有那个透过现象看本质的意味:
首先特殊情况:n 等于 1 或者 2 ,位置随便折腾,所以 c 必然可升序
一般情况:
别管细节,就粗略看序列元素的移动方向: 末尾 -> 中央 -> 末尾,对不对?这就说明结论必然跟原序列中元素的大小位置有关。那就试试呗,大伙们也可以模拟一下过程,很容易发现结论:
以 n 为偶数为例:
元素1(下标) 和 2 可看做一个整体,3 和 4 可看做一个整体,5 和 6 可看做一个整体,以此类推,每个整体内部元素大小关系无伤大雅,这个原因其实也不难想,因为在上述两个操作中有可控点:
序列 b 元素个数为奇数,元素放中左还是中右
序列 c 元素个数为偶数,元素从中左拿还是中右拿
这刚好让每个整体内部的位置关系变得可调
而最终能否构成一个升序序列只与相邻整体之间的大小关系有关,也就是 前一个整体的最大元素 必须小于 后一个整体中的较小元素
至于 n 为奇数的情况就交给读者自行思考了,基本一样~
AC代码:
#includeusing namespace std; #define N 200000 int t, n, a[N+1]; void solve() { //特判 if(n == 1 || n == 2){ cout <<"YES" < >t; while(t--) { cin >>n; for(int i = 1; i <= n; i++) cin >>a[i]; solve(); } return 0; }
F.Desktop Rearrangement 基本题意:
给定一个 n 行 m 列的只含有 "*" 和 "." 的矩阵,进行 q 次下列操作:
给出一个 x 和 y ,将 (x-行,y-列) 位置的字符翻转,即"*" -> ".", "." -> "*".
然后再询问:如果将矩阵的所有 "*" 全部排列在前几列,不满一列的在下一列自上而下排列,需要挪动几个"*"
问题分析:槽点在列排列,由于我想当然,所以浪费了大把时间搞成了行
思路很简单,暴力模拟就行(用总共的 "*" 数量 - 修改所覆盖区域内已有的 "*" 数量),毕竟最大时间限制是 3s ,不过也需要稍微维护一下,因为完全暴力的话1000*1000*1e5没准还得炸。
1.用一个 cnt 数组来记录当前状态下每一列所包含的 "*" 数量
2.用 sum 来记录当前状态下总共的 "*" 数量
这样就可以在每次查询时通过 sum 快速推断出哪几列会被修改覆盖,同时利用 cnt 数组快速计算出覆盖区域内已有的 "*" 数量 ans ,然后 sum - ans ,结果搞定
AC代码:这里我把题目中的字符矩阵改成了 bool 数组的 01 矩阵,纯属个人习惯,正好也是去掉了下标不对应的问题,其实也没啥必要
#includeusing namespace std; #define N 1000 int n, m, q; bool a[N+1][N+1]; int cnt[N+1] = {0}, sum = 0; void solve() { while(q--){ int x, y; cin >>x >>y; //维护*的总数和每一列的*数量 if(a[x][y]) cnt[y]--, sum--; else cnt[y]++, sum++; //翻转 a[x][y] = !a[x][y]; //输出结果 int yi = sum/n; //满列数 int xi = sum%n; //非满列的行数 int ans = 0; //满列的*计数 for(int i = 1; i <= yi; i++) ans += cnt[i]; //非满列的*计数 for(int i = 1; i <= xi; i++) if(a[i][yi+1]) ans++; cout < >n >>m >>q; for(int i = 1; i <= n; i++){ string s; cin >>s; //转化成01矩阵 for(int j = 0; j < s.length(); j++){ if(s[j] == '.') a[i][j+1] = false; else{ a[i][j+1] = true; cnt[j+1]++, sum++; } } } // solve(); return 0; }



