Codeforces Round #676 (Div. 2)A-D题解

Codeforces Round #676 (Div. 2)A-D题解
比赛链接:https://codeforces.com/contest/1421

A题
水,贪心,位运算

题意为给定两个正整数a和b,你需要找到一个x,使得a异或x加上b异或x的值最小。输出这个最小值。

首先把a和b全部写为二进制。
对于二进制上同一位来说,如果a在这一位上与b在这一位上的值相同,也就是同为1或者同为0,我们可以让x在这一位上的值与a和b的值相同,就可以使得最后结果的这一位上变为0,满足尽可能小这一目标。
如果a和b在这一二进制为上的值一个为1,一个为0,无论我们的x在这一位上是1还是0,最后的结果在这一位上都是1。
总结后可以得到一个规律,在二进制位上a和b相同的位,最后结果为0;不相同的位,最后结果为1。与异或运算相契合,因此直接输出a异或b即可。

#include<bits/stdc++.h>
#define ll long long
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;

int32_t main()
{
    
    
    IOS;
    int t;
    cin>>t;
    while(t--)
    {
    
    
        int a,b;
        cin>>a>>b;
        cout<<(a^b)<<endl;
    }
}

B题
构造

题意为给定一个n × \times ×n的矩阵,其中(1,1)为起点S,(n,n)为终点F,其余位置均为0或者1。
从起点出发,现在0或者1中选择一个值,之后只能走当前位置相邻且与该值相等的格子。
现在你最多改变两个格子的值,从0改为1或者从1改为0,使得从起点无法有路径到达终点。

这里的话要注意到,我们的可操作次数非常少,最多只有2次,因此我们必然要改变关键的格子位置。
对于起点和终点来说,与他们相邻的分别有两个格子,这4个格子是最为关键的。
我们只需要让起点周围的两个格子的值相同,终点周围的两个格子的值相同,且起点和终点周围的格子值不相同。这样的话从起点出发就必须只能选择起点周围格子的那个值,而这个值是必然走不到终点的。

按照这种构造思路,按照起点终点周围格子值的情况再分成三种情况讨论:
1.起点周围两个格子的值相等,我们只需要将终点周围的两个格子变为与起点不同即可。
2.终点周围两个格子的值相等,我们只需要将起点周围的两个格子变为与终点不同即可。
3.不满足上述两种情况下,起点周围的两个格子一个为1一个为0,终点周围的两个格子同样一个为1一个为0,我们把起点周围的1变为0,终点周围的0变为1即可。

#include<bits/stdc++.h>
#define ll long long
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;

string field[207];
bool right_start,down_start;
bool up_end,left_end;

int32_t main()
{
    
    
	IOS;
	int t;
	cin>>t;
	while(t--)
	{
    
    
		int n;
		cin>>n;
		for(int i=0;i<n;i++) cin>>field[i];
		right_start=field[0][1]-'0';
		down_start=field[1][0]-'0';
		up_end=field[n-2][n-1]-'0';
		left_end=field[n-1][n-2]-'0';
		vector<pair<int,int>>ans;
		if(right_start==down_start)//如果起点的右侧和下侧两个点的值相同
		{
    
    
			if(up_end==right_start) ans.push_back({
    
    n-1,n});//将终点上侧和左侧点的值变为与起点周围不同
			if(left_end==right_start) ans.push_back({
    
    n,n-1});
		}
		else if(up_end==left_end)//如果终点上侧和左侧点的值相同
		{
    
    
			if(right_start==up_end) ans.push_back({
    
    1,2});//将起点右侧和下侧的两个点的值变为与终点周围不同
			if(down_start==up_end) ans.push_back({
    
    2,1});
		}
		else//剩余的情况就是起点周围的两个点不相同,且终点周围的两个点周围都不相同的情况
		{
    
    
			if(right_start) ans.push_back({
    
    1,2});//我们把起点周围的1变为0,只会有1次操作
			if(down_start) ans.push_back({
    
    2,1});
			if(!up_end) ans.push_back({
    
    n-1,n});//把终点周围的0变为1,同样只会有1次操作
			if(!left_end) ans.push_back({
    
    n,n-1});
		}
		cout<<ans.size()<<endl;
		for(int i=0;i<ans.size();i++) cout<<ans[i].first<<' '<<ans[i].second<<endl;
	}
}

C题
字符串构造

题意为给定一个长度至少为3,最大为1e5的字符串S,你需要在30次操作内使得这个字符串变为回文串,且长度不超过1e6。
你可以执行两种操作,令length为字符串的当前长度
第一种操作,选择一个下标x,x在2到length-1之间(闭区间),将下标2到x的子串翻转后复制到字符串头部。
第二种操作,选择一个下标x,x在2到length-1之间(闭区间),将下标x到length-1的子串翻转后复制到字符串尾部。

注意到这个操作方式得到的字符串,有个恶心的点,如果我们选择操作2,下标选择2的话,类似字符串abcd会变成abcdcb,就因为复制的部分少了下标1的这个字符,导致无法构成回文串。
那么我们可以想到,如果原字符串是ababcd这样的形式(头部第一个字符和第三个字符相同),操作2下标选择2后,字符串是ababcdcbab,我们再执行操作2,下标选择length-1,就是ababcdcbaba回文串了。
那么我们能否将任意的字符串都构造成头部第一个字符和第三个字符相同的形式呢?我们可以通过一次简单的操作1,下标选择2,就可以得到的这样的字符串。

因此执行上述的三次操作,就可以将任意给定的字符串变为回文串,且长度变为原来的两倍加一,必然在1e6之内。

#include<bits/stdc++.h>
#define ll long long
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;

int32_t main()
{
    
    
    IOS;
    string s;
    cin>>s;
    cout<<3<<endl;
    cout<<'L'<<' '<<2<<endl;
    cout<<'R'<<' '<<2<<endl;
    cout<<'R'<<' '<<s.size()*2-1<<endl;
}

D题
贪心,分类讨论

题意为在一个六边形的蜂房上,起点在(0,0),告诉你终点位置,并且告知朝着六个方向移动的消耗,询问最小消耗是多少。

这里先认识到,六个方向分别对应六种坐标的改变方式,先用c[]数组存储下来。
c[0]对应(+1,+1)
c[1]对应(0,+1)
c[2]对应(-1,0)
c[3]对应(-1,-1)
c[4]对应(0,-1)
c[5]对应(+1,0)
之后再根据贪心的策略可以知道,c[0]和c[3]操作,c[2]和c[5]操作这些都是不可能同时使用的,因为这样走了回头路,必然不是最优的了。

我们可以列出这样的组合方式:
1.(+1,0)和(-1,0)中的一种与(0,+1)和(0,-1)中的一种组合,直接横纵坐标割裂开走到终点。
2.(+1,+1)与(+1,0)或者(0,+1)组合,处理终点横纵坐标均大于0的情况,此处必然要优先使用(+1,+1)否则横坐标或者纵坐标中必然有一个是无法被另一个操作改变的。
3.(-1,-1)与(-1,0)或者(0,-1)组合,处理终点横纵坐标均小于0的情况,此处必然要优先使用(-1,-1)否则横坐标或者纵坐标中必然有一个是无法被另一个操作改变的。(后面的情况都是如此,不再赘述)
4.(+1,+1)与(0,-1)组合,处理终点横坐标不小于0,且纵坐标不大于横坐标的情况
5.(+1,+1)与(-1,0)组合,处理终点纵坐标不小于0,且横坐标不大于纵坐标的情况
6.(-1,-1)与(+1,0)组合,处理终点纵坐标不大于0,且横坐标不小于纵坐标的情况
7.(-1,-1)与(0,+1)组合,处理终点横坐标不大于0,且纵坐标不小于横坐标的情况
除了上述的两两组合外,其他所有的两两组合均存在前面讨论的回头路情况,不是最优。并且上述的两两组合中再增加任何一个不同的第三种操作,同样会形成回头路。因此讨论上述情况即可。

#include<bits/stdc++.h>
#define ll long long
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;

int32_t main()
{
    
    
	IOS;
	int t;
	cin>>t;
	while(t--)
	{
    
    
		ll x,y;
		cin>>x>>y;
		ll c[6];
		for(int i=0;i<6;i++) cin>>c[i];
//		c[0]对应(+1,+1)
//		c[1]对应(0,+1)
//		c[2]对应(-1,0)
//		c[3]对应(-1,-1)
//		c[4]对应(0,-1)
//		c[5]对应(+1,0)
		ll ans=0;
		if(x>0) ans+=x*c[5];//直接选择c[2]或者c[5],再使用c[1]或者c[4]所需要的消耗
		else ans-=x*c[2];
		if(y>0) ans+=y*c[1];
		else ans-=y*c[4];
		if(x>=0&&y>=0)//处理终点横纵坐标均大于0的情况
		{
    
    
			ll temp=min(x,y)*c[0];
			if(x>y) temp+=(x-y)*c[5];
			else temp+=(y-x)*c[1];
			ans=min(ans,temp);
		}
		if(x<=0&&y<=0)//处理终点横纵坐标均小于0的情况
		{
    
    
			ll temp=-max(x,y)*c[3];
			if(x<y) temp+=(y-x)*c[2];
			else temp+=(x-y)*c[4];
			ans=min(ans,temp);
		}
		if(x>=0&&y<=x) ans=min(ans,c[0]*x+c[4]*(x-y));//处理终点横坐标不小于0,且纵坐标不大于横坐标的情况
		if(y>=0&&x<=y) ans=min(ans,c[0]*y+c[2]*(y-x));//处理终点纵坐标不小于0,且横坐标不大于纵坐标的情况
		if(y<=0&&x>=y) ans=min(ans,-c[3]*y+c[5]*(x-y));//处理终点纵坐标不大于0,且横坐标不小于纵坐标的情况
		if(x<=0&&y>=x) ans=min(ans,-c[3]*x+c[1]*(y-x));//处理终点横坐标不大于0,且纵坐标不小于横坐标的情况
		cout<<ans<<endl;
	}
}

猜你喜欢

转载自blog.csdn.net/StandNotAlone/article/details/109234152