【前行&赛时总结】◇第2站&赛时·8◇ Atcoder ABC-109

 

【第2站&赛时·8】 ABC-109

把最后一题题意理解错了……在第二组数据卡了好久(然而并不知道是special judge)QwQ

最终AK,速度慢了一些 Rank:357 Rating:1228(+41)

 

 


 

◇ 简单总结

第一二题如往常一样水过去了,然后发现第三题也不怎么难,题目看懂过后就只是一个数学的GCD,只是代码写完过后不知道为什么断网了,差不多六七分钟才提交代码TAT……

第四题就是一道奇妙的贪心构造题(我觉得应该是这样),只是一开始没读懂题目,一直在纠结为什么第二组样例为什么要进行最后一次操作,最后发现其实不需要满足操作次数最少?,然后就AK了。

不得不说,蓝名是靠着ABC一点一点蹭上去的,不过上了蓝色,就意味着我不再是一个“Beginner”了,接下来便是 ARC 和 AGC 的历练了?

哈~可真是不容易?


 

◇ 题目&解析

◆A题◆ ABC333 ❄数学❄ +传送门+

【题意】

给出在1~3的整数A,B,求是否有一个1~3的整数C满足A*B*C为奇数。存在输出“Yes”否则输出“No”。

【解析】

由于 偶*奇=偶 且 偶*偶=偶 ,所以A,B中只要出现一个偶数,则最后的结果都会是偶数,那么我们就是要判断 A,B 是否都是奇数,是则为Yes,只要有一个不是奇数,则是No。

【源代码】

/*Lucky_Glass*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int main() {
	int A,B;
	scanf("%d%d",&A,&B);
	if(A%2 && B%2) printf("Yes\n");
	else printf("No\n");
	return 0;
}

◆B题◆ Shiritori ❄模拟❄ +传送门+

【题意】

一个单词接龙游戏:①同一个单词不能出现多次;②除第一个单词,第i个单词开头字母必须是第i-1个单词末尾字母;

给出n个单词,判断“按照输入顺序说出单词,是否满足游戏要求”。

【解析】

n不大,没有必要用字符串hash,直接用map映射判重。用last存储上一个单词的末尾字符(初始值为第一个单词的末尾字符),从第一个单词后判断单词开头是否是last,并每次更新last为当前单词末尾。

【源代码】

/*Lucky_Glass*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<iostream>
using namespace std;
int n;
string str;
map<string,bool> vis;
int main() {
	scanf("%d",&n);
	char last;
	cin>>str;
	last=str[str.length()-1];
	vis[str]=true;
	for(int i=1; i<n; i++) {
		cin>>str;
		if(vis[str] || str[0]!=last) {
			printf("No\n");
			return 0;
		}
		last=str[str.length()-1];
		vis[str]=true;
	}
	printf("Yes\n");
	return 0;
}

  

◆C题◆ Skip ❄数学计算❄ +传送门+

【题意】

有n座城市位于一条数轴上,没有两个城市的坐标相同。现在你站在坐标X(X不在任何一个城市),你每次可以向左或向右移动K个单位(即+K或-K)。求出一个最大的K,使得你能从X到达每一个城市。

给出n,X,以及每个城市的坐标 xi。求最大的K。

【解析】

如果要从X能够到达每一个城市,则X与各城市的距离都是K的倍数!由于K要最大,则K是X与各城市距离的最大公因数。

话是这么说,但是我比赛的时候有点糊涂,用的是另外一种方法?——把X也看成一个城市,一开始位于最左边的城市,一直向右跳,则应该每次都从第i个城市到第i+1个城市,所以K必须是所有相邻的城市的距离的最大公因数。实现就比一开始说的方法复杂……

【源代码】

/*Lucky_Glass*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
typedef long long ll;
ll GCD(ll a,ll b) {
	ll r;
	while(b) {
		r = b;
		b = a % b;
		a = r;
	}
	return a;
}
int n;
set<ll> num;
int main() {
	scanf("%d",&n);
	for(int i=0,pos;i<=n;i++)
		scanf("%d",&pos),
		num.insert(1ll*pos);
	ll las=0,ans;
	for(set<ll>::iterator it=num.begin();it!=num.end();it++){
		ll val=*it;
		if(!las) {las=val;continue;}
		if(las==*num.begin()) ans=val-las;
		else ans=GCD(ans,val-las);
		las=val;
	}
	printf("%lld\n",ans);
	return 0;
}

  

◆D题◆ Make Them Even ❄贪心+构造❄ +传送门+

【题意】

有一个r*c的矩阵,矩阵的每个点上都放了0~9个硬币。你可以进行0~r*c次操作:选取一个点(x,y),满足这个点在之前的操作中没有选中过,且该点上有硬币,然后将(x,y)的一个硬币移动到它周围的点上(四连通,上下左右)。所有操作完成后,使得矩阵中硬币个数为偶数的点的数量最大。注意:不要求操作次数最少

输出操作次数m,再输出m行,每行包含整数 x,y,x',y',描述一次将(x,y)的一个硬币移动到(x',y')上的一次操作。

【解析】

首先是几个简单的贪心思想:

① 不移动为偶数的硬币;

② 不重复移动,即将(x,y)移动到(x',y')又将(x',y')移动到(x,y);

③ 如果矩阵所有的硬币总和是奇数,则最优解一定只有一个点是奇数,否则最优解的所有点都是偶数。

第③个结论是如何得来的呢?其实这就是得出答案的过程——

想象成一个推硬币游戏?……从第一列开始推硬币,往右推,一直推到倒数第二列……设当前推到了第j列,若(i,j)是奇数,则把(i,j)的一个硬币推到(i,j+1)。因为最后一列的右边没有点,就不推了。这样推一次后,第一列到倒数第二列都会变成偶数(只要是奇数就推走一个,就变成了偶数)。那么我们可以想到,此时最后一列可能仍然存在奇数,那么我们从最后一列的第一行开始往下推,类似与之前推的规则,如果(i,c)为奇数,则向下(i+1,c)推,这样(i,j)就变成了偶数。因为点(r,c)已经没有地方推了,此时的奇偶性就确定了。容易理解,点(r,c)的奇偶性和矩阵中硬币总数的奇偶性是一致的。

上述“推”就是一次操作。为什么这就是最优解?考虑一次将(x,y)移动到(x',y')的操作——操作前(x,y)为奇数,那么操作后(x,y)为偶数,如果操作前(x',y')为奇数,则操作后为偶数,矩阵中偶数点个数+2,如果操作前为偶数,则操作后为奇数,矩阵中偶数点的个数不变。如果我们每次都对奇数点进行操作,可以想到,矩阵中要么只剩一个奇数点,要么没有奇数点,此时为最优解(因为无论如何都无法使偶数点变多了)。

【源代码】

 

/*Lucky_Glass*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N=500;
struct ANS {
	int a,b,c,d;
	ANS(int x,int y,int z,int w):a(x),b(y),c(z),d(w) {};
};
vector<ANS> ans;
int r,c;
int val[N+5][N+5];
bool vis[N+5][N+5];
int main() {
	scanf("%d%d",&r,&c);
	for(int i=0; i<r; i++)
		for(int j=0; j<c; j++)
			scanf("%d",&val[i][j]),
			val[i][j]&=1; //只保存奇偶性
	for(int i=0; i<r; i++) //推第一列到倒数第二列
		for(int j=0; j<c-1; j++) //把(i,j)推到(i,j+1)
			if(val[i][j]) //只操作奇数点
				val[i][j+1]^=val[i][j],
				ans.push_back(ANS(i+1,j+1,i+1,j+2)); //保存操作
	for(int i=0; i<r-1; i++) //推最后一列
		if(val[i][c-1]) //之推奇数
			val[i+1][c-1]^=val[i][c-1],
			ans.push_back(ANS(i+1,c,i+2,c)); //保存操作
	//P.S. "^"是异或,这里用来改变奇偶性
	printf("%d\n",ans.size());
	for(int i=0; i<ans.size(); i++)
		printf("%d %d %d %d\n",ans[i].a,ans[i].b,ans[i].c,ans[i].d);
	return 0;
}

  


 

◇ 感悟

不再是一个Beginner了……该拿ARC练练手了?。或许是一个新的起点,新的goal吧!

 


 

The End

Thanks for reading!

- Lucky_Glass

(Tab:如果我有没讲清楚的地方可以直接在邮箱lucky_glass@foxmail.com email我,在周末我会尽量解答并完善博客~?)

转载于:https://www.cnblogs.com/LuckyGlass-blog/p/9613395.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值