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-1;
2、当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分别为64和62的时候第一次变化为62和2,随后每次较小的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;
}