Codeforces Round 885 (Div. 2)

A. Vika and Her Friends

题意:题目给出n*m的矩阵,给出一个人的初始坐标,然后给出k个朋友的所在坐标,每次该人和他的所有朋友都必须移动到四个方向的临点,询问在无数次移动后,该人是否会和任意一个朋友所在坐标相同

思路:可以将矩阵想象成类似国际象棋棋盘的黑白方格矩阵,每次移动都必须移动到周围四个不同颜色的临格,假设该人的朋友与该人的初始坐标颜色不同,则他们每次移动都需要换颜色,这就导致他们永远不可能会相遇,而这种矩阵有如下性质:两种不同颜色的坐标值x+y的奇偶性不同,所以只需要判断是否有一个朋友的的坐标和的奇偶性与该人相同即可

该种矩阵还有一个性质,同种颜色之间的距离一定为偶数,所以判断是否有一个朋友到该人距离是否为偶数也可

ac代码:

#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define INF 0x3f3f3f3f
#define pb push_back
#define int long long
#define Mirai ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
using namespace std;
typedef pair<int,int> pii;
const int N=2e5+10;
int n,m,k;
int a,b;
int x,y;
void solve()
{
	cin>>n>>m>>k;
	cin>>a>>b;
	bool flag=true;
	for(int i=0;i<k;i++)
	{
		cin>>x>>y;
		if((abs(a-x)+abs(b-y))%2==0)flag=false;
	}
	if(flag)cout<<"YES"<<endl;
	else cout<<"NO"<<endl;
}
signed main()
{
	Mirai;
	int T=1;
	cin>>T;
	while(T--)
	{
		solve();
	}
}

B. Vika and the Bridge

题意:题目给出n和m,随后给出包含n个数的数组,其中数组中的数一定为1-m中的一个,要求寻找一组相同的数,两两之间的距离以及两端的两个数到数组两端的所有距离中的最大值最小,在寻找之前可以进行一次以下操作:将数组中的任意一个数修改为其他任意一个数

思路:既然数组中的数的范围是给出的,所以可以用vector数组将每组数的下标单独存下来,然后求该组数组成的所有间距,再对该间距数组进行排序,修改操作就可以等同于将最大的距离-1然后平分为两个距离(其中较大的距离为原距离/2),然后所有距离中的最大距离此时有两种情况:1、最大距离/2,2、原次大距离,取max即可

trick:特判第一个点和最后一个点求间距re了,可以在所有数组前加一个0,末尾加一个n+1,便于通法求距离,避免re

ac代码:

#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define INF 0x3f3f3f3f
#define pb push_back
#define int long long
#define Mirai ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
using namespace std;
typedef pair<int,int> pii;
const int N=2e5+10;
int n,k;
vector<int> a[N];
void solve()
{
	cin>>n>>k;
	for(int i=0;i<=k;i++)a[i].clear();
	for(int i=0;i<=k;i++)a[i].pb(0);
	for(int i=1;i<=n;i++)
	{
		int co;
		cin>>co;
		a[co].pb(i);
	}
	for(int i=0;i<=k;i++)a[i].pb(n+1);
	int res=INF;
	for(int i=1;i<=k;i++)
	{
		vector<int> temp;
		for(int j=1;j<a[i].size();j++)
		{
			temp.pb(a[i][j]-a[i][j-1]-1);
		}
		sort(temp.begin(),temp.end());
		int x=temp.back();
		temp.pop_back();
		int xx=temp.back();
		int ans=max(xx,x/2);
		res=min(res,ans);
	}
	cout<<res<<endl;
}
signed main()
{
	Mirai;
	int T=1;
	cin>>T;
	while(T--)
	{
		solve();
	}
}

C. Vika and Price Tags

题意:输入给出n个数的数组a和b,对于两个数组我们可以进行以下操作来创建一个新数组c,数组c的元素组成:ci=|ai-bi|,询问是否存在让数组a全为0的情况,

思路:通过观察可以发现,任何两个数在不断操作之后最终都会进入(0,x,x)的周期为3的循环,所以我们只需要判断所有的数对(a[i],b[i]),是否会在同一个操作中出现0,也就是第一次出现0的操作次数%3是否相等,而且对于两个数的操作序列与这两个数同除gcd之后的操作序列是等价的

例如:6 4 2 2 0和3 2 1 1 0

而两个偶数同除最大公约数之后一定会出现一个奇数,然后两数相减就只有以下三种情况

1、奇数-奇数=偶数

2、奇数-偶数=奇数

3、偶数-奇数=奇数

不难发现任意两个数的操作序列是在这三中情况中循环,周期同样为3,由于同除gcd不会出现连续两个偶数,所以(0,x,x)的循环中两个x一定是奇数,然后0占据了原先偶数的位置,所以最初两个数的奇偶性决定了0在何时出现,因此只需要判断最初所有数对是否都是同一种状态即可,注意特判两个数都为0的情况,00的操作序列为全0

ac代码:

#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define INF 0x3f3f3f3f
#define pb push_back
#define int long long
#define Mirai ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
using namespace std;
typedef pair<int,int> pii;
const int N=1e5+10;
int gcd(int a,int b)
{
	return b?gcd(b,a%b):a;
}
int n;
int a[N],b[N];
void solve()
{
	cin>>n;
	for(int i=0;i<n;i++)cin>>a[i];
	for(int i=0;i<n;i++)cin>>b[i];
	bool t1=false,t2=false,t3=false;
	for(int i=0;i<n;i++)
	{
		if(a[i]==0&&b[i]==0)continue;
		int x=gcd(a[i],b[i]);
		if(x)
		{
			a[i]/=x;
			b[i]/=x;
		}
		if(a[i]%2!=0&&b[i]%2!=0)t1=true;
		else if(a[i]%2==0&&b[i]%2!=0)t2=true;
		else t3=true;
	}
	int cnt=0;
	if(t1)cnt++;
	if(t2)cnt++;
	if(t3)cnt++;
	if(cnt==0||cnt==1)cout<<"YES"<<endl;
	else cout<<"NO"<<endl;
}
signed main()
{
	Mirai;
	int T=1;
	cin>>T;
	while(T--)
	{
		solve();
	}
}

D. Vika and Bonuses

题意:题目输入给出两个数s,k,一共有k次操作次数,操作有两种选择:1、获得s价值,2、将s+(s%10),询问k次操作后可以获得的最大价值

思路:这题就是一个抛物线求最大值,关于s+(s%mod)是有些性质的,都写在代码里了

ac代码:

#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define INF 0x3f3f3f3f
#define pb push_back
#define int long long
#define Mirai ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
using namespace std;
typedef pair<int,int> pii;
const int N=1e6+10;
//a+(a%10)具有以下性质
//如果a%10==0则a不变
//如果a%10==5,则加一次a增大,之后不再增大
//如果a%10==2,则会进入a%10=2,4,8,6的循环
//其余情况在最多加一次后也会进入2386的循环中,注意到这些情况全为奇数
int f(int s,int k)
{
    //  抛物线公式
	//  (s + 20x) * (k - 4x)
    //  -b/2a = (5k-s)/40
    int x=(5*k-s)/40;//最高点下取整
    x=min(x,k/4);//考虑k不足以支持取4x次
    int res=s*k;//存在最高点在x负半轴的情况,自然最大值在y轴上取到
    if(x>0)res=max(res,(s+20*x)*(k-4*x));//对两种情况取max
    x=min(x+1,k/4);//对最高点x上取整
    if(x>0)res=max(res,(s+20*x)*(k-4*x));//再取max
    return res;
}
void solve()
{
    int s,k;
    cin>>s>>k;
    int ans=s*k;//一次也不加的情况的结果
    if(s%10==5)ans=max(ans,(s+5)*(k-1));//如果末尾为5,则与加一次取max
    else if(s%10)//如果末尾不为0
    {
        if(s%2==1)//如果末尾位奇数,则要先取一次使之进入2486循环
        {
            s+=(s%10);
            k--;//此处不用对结果取max,因为接下来的循环会包括一次也不取的情况
        }
        for(int i=0;i<4;i++)//遍历以四个数为循环起点的情况,或者说为终点的情况,所以自然包括所有取法
        {
            if(k>0)//此处k有可能取到负数,所以保证大于0
            {
                ans=max(ans,f(s,k));//对结果取max
                s+=(s%10);
                k--;
            }
        }
    }
    cout<<ans<<endl;
}
signed main()
{
    Mirai;
    int T=1;
    cin>>T;
    while(T--)
    {
        solve();
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值