Codeforces Round #786 (Div. 3) A~D F

着实好久没打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代码:

#include<bits/stdc++.h>
using namespace std;
int t, n, a, b;
void solve()
{
	if(b % a != 0){
		cout <<0 <<" " <<0 <<endl;
		return ;
	}
	cout <<1 <<" " <<b/a <<endl;
}
int main(void)
{
	cin >>t;
	while(t--)
	{
		cin >>a >>b;
		solve();
	}
	return 0;
} 

B.Dictionary

基本题意:

等价于没有题意~

问题分析:

就是简单的两位字母 a~z 排序(ab ac ad ... ba bc ... zx zy),顺便附加条件:两位字母不能相同

这复杂度想暴力暴力,不想暴力就直接列算式算呗,计算过程应该不需要解释吧

AC代码:

#include<bits/stdc++.h>
using namespace std;
int t, n, a, b;
string s;
void solve()
{
	int res = (s[0]-'a') * 25 + (s[0]<s[1] ? (s[1]-'a') : (s[1]-'a'+1));
	cout <<res <<endl;
}
int main(void)
{
	std::ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	
	cin >>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代码:

#include<bits/stdc++.h>
using namespace std;
int T, n, a, b;
string s, t;
void solve()
{
	//长度为1且为a
	if(t.length() == 1 && t[0] == 'a'){
		cout <<1 <<endl;
		return;
	}
	//判断t串是否含有a
	bool ifa = false;
	for(int i = 0; i < t.length(); i++){
		if(t[i] == 'a'){
			ifa = true; break;
		}
	}	
	if(ifa){
		cout <<-1 <<endl;
	}else{
		long long res = pow(2, s.length());
		cout <<res <<endl;
	}
}
int main(void)
{
	std::ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	
	cin >>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代码:

#include<bits/stdc++.h>
using namespace std;
#define N 200000
int t, n, a[N+1];
void solve()
{
	//特判
	if(n == 1 || n == 2){
		cout <<"YES" <<endl;
		return;
	}
	//初始化比较对象
	bool ans = true; int be;
	if(n & 1) be = a[1];
	else be = max(a[1], a[2]);
	//比较
	for(int i = ((n & 1) ? 2 : 3); i <= n; i += 2){
		//发现逆序,直接退出
		if( min(a[i], a[i+1]) < be ){
			ans = false;
			break;
		}
		//更新
		be = max(a[i], a[i+1]);
	}	

	if(ans) cout <<"YES" <<endl;
	else cout <<"NO" <<endl;
}
int main(void)
{
	std::ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	
	cin >>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 矩阵,纯属个人习惯,正好也是去掉了下标不对应的问题,其实也没啥必要

#include <bits/stdc++.h>
using 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 <<sum - ans <<endl;
    }
}
 
int main(void)
{
    std::ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    //数据读入及基本处理
    cin >>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;
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UmVfX1BvaW50

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值