Educational Codeforces Round 117 (Rated for Div. 2)题解(A~D)

Educational Codeforces Round 117 (Rated for Div. 2)

今天这场没打,赛后从九点半到十一点把前面四个题目给补了一下,E题明天有时间看看能不能弄出来。

A. Distance

在这里插入图片描述

今天没有谜语人,题意还是很好懂的;

题意:对于T组测试样例,每组给两个数x,y,为一个坐标,要求找出一个点,该点到原点(0,0)和给定点(x,y)的曼哈顿距离相等;所谓曼哈顿距离,即为两点之间x轴距离和y轴距离之和,若该点找不到,则输出-1;

解题思路:首先判断该点是否能找到,若坐标都是偶数,如果给定的点到原点的曼哈顿距离为奇数,那么不可能存在一个点到这两个点的曼哈顿距离相等,此时输出-1;
若给出的坐标x,y都为偶数则直接找(0,0)到(x, y)的中点即为(x/2,y/2)为答案;
若给出的x,y都是奇数,只需要保持较小的轴距不变,较大的轴变成(x+y)/2-y(此时x>y);

AC代码:

#include<iostream>
#include<algorithm>
using namespace std;
 
void solve(){
    int x, y;
    scanf("%d%d", &x, &y);
    if((x+y)%2 == 1){
        puts("-1 -1");
        return;
    }
    if(x%2 == 0||x%2 == 0){
        printf("%d %d\n", x/2, y/2);
    }
    else {
        if(x<y) printf("%d %d\n", x, (y-x)/2);
        else printf("%d %d\n", (x-y)/2, y);
    }
}
 
int main()
{
    int t;
    scanf("%d", &t);
    while(t--){
        solve();
    }
    return 0;
}

B. Special Permutation

在这里插入图片描述
题意:每组数据给定三个值,n,a,b,其中n为偶数,表示要构造一个长度为n的全排列数组,a,b表示构造的数组前半部分最小值是a,后半部分最大值为b,若能构造,输出数组,否则输出-1;

解题思路:一个小贪心分前后两部分直接构造,前半部分依次从n找到a越大越好找n/2-1个数字,后半部分依次从1找到b越小越好,外面开个桶保证数据不重复,最后遍历桶,有数据没有用到表示无法构造,输出-1,否则直接输出数组;

AC代码:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
 
const int N = 105;
int a[N];
int st[N];
 
void solve(){
    memset(st, 0, sizeof st);
    int n, x, y;
    scanf("%d%d%d", &n, &x, &y);
    st[x] = 1, st[y] = 1;
    // if(x>(n/2+1)||y<n/2){
    //     puts("-1");
    //     return;
    // }
    a[1] = x;
    int i, j;
    for(i = 2, j = n; i<=n/2&&j>x; j--){
        if(!st[j]){
            a[i] = j;
            st[j] = 1;
            i++;
        }
    }
    a[n/2+1] = y;
    for(i = n/2+2, j = 1; i<=n&&j<y; j++){
        if(!st[j]){
            a[i] = j;
            st[j] = 1;
            i++;
        }
    }
    for(int i = 1; i<=n; i++){
        if(!st[i]){
            puts("-1");
            return;
        }
    }
    for(int i = 1; i<=n; i++){
        printf("%d ", a[i]);
    }
    puts("");
    return;
}
 
int main()
{
    int t;
    scanf("%d", &t);
    while(t--){
        solve();
    }
    return 0;
}

C. Chat Ban

在这里插入图片描述
题意:你要发送2*k-1条消息,每条消息包含一定数量的表情符号,每行的符号从1递增到k再递减到1,所以有k-1行,但是当你发送到第x个表情的时候,会终止你的发送,求你能发送多少行;

解题思路:每一行有特定的数值,现在用第i行代表前i行所有表情符号的个数之和;求第一个大于等于x的i,前缀和数组有单调性,妥妥的二分搜索,但是该题的x数据范围略高,不能用数组存储直接二分;
我们对于本题1~k中的每行,求第i行的前缀和,即为1+2+3……+i,可以直接用公式算出来(公式:i*(i+1)/2)这个公式相信小学都见过,不做推导;

由于前k行有等差递增规律,后k-1行有等差递减规律,将两部分分开来看;
接下来求出前k行的总数ans = k*(k+1)/2,和后k-1行的总数res = k*(k-1)/2;
之后就是分类讨论了:
	1、当x>=ans+res,此时x无法限制,直接输出2*k-12、当x<=ans的时候,表示发送数量不会超过k行,直接对前k行做二分,
		找到第一个大于等于x的行数;
		
	3、当x>ans的时候, 表示前k行一定会发送,此时对后k-1行进行二分,再加上k; 或者二分不会发送的行数,再用总行数减去即为所求;
	

AC代码:

#include<iostream>
#include<algorithm>
using namespace std;
 
typedef long long ll;
 
void solve(){
    ll x, y;
    scanf("%lld%lld", &x, &y);
    ll ans = x*(x+1)/2;
    ll num = x*(x-1)/2;
    if(ans+num<=y){
        printf("%lld\n", x*2-1);
        return;
    }
    if(y<=ans){
        ll l = 0, r = x;
        while(l<r){
            ll mid = l+r>>1;
            ll res = mid*(mid+1)/2;
            if(res>=y) r = mid;
            else l = mid+1;
        }
        printf("%lld\n", l);
    }
    else{//这里为了方便,反向二分找出不会出现的行数,再用总行数减去不会出现的行数
        y = ans+num-y;
        ll l = 0, r = x-1;
        while(l<r){
            ll mid = l+r+1>>1;
            ll res = mid*(mid+1)/2;
            if(res>y) r = mid-1;
            else l = mid;
        }
        printf("%lld\n", x+x-1-l);
    }
}
 
int main()
{
    int t;
    scanf("%d", &t);
    while(t--){
        solve();
    }
    return 0;
}

D. X-Magic Pair

在这里插入图片描述
题意: 每组测试样例给定三个数a, b, x;
每次可以对a或b进行操作使得他们其中一个等于a,b两数的差值,即a = |a-b| 或 b = |a-b|; 求a或b是否能变成x;

解题思路

首先观察题目给的操作,可以发现,每次变换的数都是a和b中较大的那个数字;
证明:
	令a>b, ans = a-b;
	若令a = ans,则a2 = b, b2 = ans;
	若令b = ans,则a1 = a, b1 = ans;
	后续为a-ans和a或a-ans和ans,即为b和a,b和ans,其中b和ans为最初令a = ans的情况,所以此情况无效;

每次变化的都是较大的数字,而且变化是有规律的,
例如当a和b分别为6462的时候第一次变化为622,随后每次较小的2都不变,
较大的62每次减去2,知道2变为更大的数,此时的变化就有规律可循;

最开始令a为较大的值,a会和b一直进行类似上述描述的操作,直到a变成a%b,
若是x在a和b之间,只需要判断a和x对于b的余数是不是相同的,即a%b == x%b; 
若x小于b,a变为a%b此时a<=b,交换a、b的值令a始终为较大的数,重复上述操作,直至判断或者b变成0;
若x大于a,无法实现,输出NO;
可以用递归实现,我是直接写循环;

AC代码:

#include<iostream>
#include<algorithm>
using namespace std;
 
typedef long long ll;
 
void solve(){
    ll x, y, n;
    scanf("%lld%lld%lld", &x, &y, &n);
    if(y>x)swap(x, y);
    if(n>x){
        puts("NO");
        return;
    }
    while(1){
        if(y == 0){
            if(x == n)puts("YES");
            else puts("NO");
            return;
        }
        if(y<n){
            if(x%y == n%y){
                puts("YES");
                return;
            }
            else {
                puts("NO");
                return;
            }
        }
        if(y>=n){
            ll temp = x%y;
            x = y;
            y = temp;
        }
    }
}
 
int main()
{
    int t;
    scanf("%d", &t);
    while(t--){
        solve();
    }
    return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值