目录
A. A Variety of Operations
题目: https://codeforces.com/contest/1556/problem/A
题型: 数学
题意:
给定两个正整数数
c
c
c和
d
d
d,
a
=
0
a=0
a=0、
b
=
0
b=0
b=0,每次可以选择任意一个正整数k,使得
1、
a
=
a
+
k
a = a + k
a=a+k,
b
=
b
+
k
b = b + k
b=b+k;
2、
a
=
a
+
k
a = a + k
a=a+k,
b
=
b
−
k
b = b - k
b=b−k;
3、
a
=
a
−
k
a = a - k
a=a−k,
b
=
b
+
k
b = b + k
b=b+k。
求至少需要几次使得
a
=
c
a = c
a=c,
b
=
d
b = d
b=d。
输出最少的次数;如果不可能,则输出-1。
思路:
1、当
c
=
0
c = 0
c=0、
d
=
0
d = 0
d=0时,显然答案为0。
2、当
c
=
d
≠
0
c = d ≠ 0
c=d=0时,显然答案为1。
3、当
c
c
c和
d
d
d一奇一偶时,显然不可能,所以答案为-1。
4、(1)当
c
c
c和
d
d
d同奇同偶时,取
k
=
c
+
d
2
k = \frac{c+d}{2}
k=2c+d,进行操作1,那么
a
=
c
+
d
2
a=\frac{c+d}{2}
a=2c+d,
b
=
c
+
d
2
b=\frac{c+d}{2}
b=2c+d。
(2)若
c
>
d
c>d
c>d,取
k
=
c
−
d
2
k = \frac{c-d}{2}
k=2c−d,进行操作2,若
c
<
d
c<d
c<d,取
k
=
c
−
d
2
k = \frac{c-d}{2}
k=2c−d,进行操作3。
通过这两步就能使得
a
=
c
a=c
a=c,
b
=
d
b=d
b=d。所以答案为2。
AC代码
#include <iostream>
using namespace std;
int main(){
int t;
scanf("%d", &t);
while (t--){
int c, d;
scanf("%d%d", &c, &d);
if (c == 0 && d == 0){//c和d都等于0
printf("0\n");
continue;
}
if (c == d){//c和d相等
printf("1\n");
continue;
}
if ((c + d) % 2 == 1){//一奇一偶
printf("-1\n");
continue;
}
printf("2\n");//同奇同偶
}
return 0;
}
B. Take Your Places!
题目: https://codeforces.com/contest/1556/problem/B
题型: 模拟
题意:
给定一个序列,每次可以交换相邻的两个数,求最少需要多少次,使得序列变成成奇偶相间的序列。
输出最少次数;如果不可以,则输出-1。
思路:
设位置下标为0 ~ n-1。
用cnt1表示奇数的个数,用cnt2表示偶数的个数。
1、当
∣
c
n
t
1
−
c
n
t
2
∣
>
1
\lvert cnt1 - cnt2 \rvert > 1
∣cnt1−cnt2∣>1时,显然不可能做到奇偶相间,所以输出-1。
2、当
c
n
t
1
−
c
n
t
2
=
1
cnt1 - cnt2 = 1
cnt1−cnt2=1时,排列方法为奇 偶 奇 偶 ...... 奇
。所以只需要遍历一遍
c
n
t
1
cnt1
cnt1,计算第i个奇数的位置与
(
2
i
)
(2i)
(2i)的绝对值之差,最终最少次数就是这些差的和。
3、当
c
n
t
1
−
c
n
t
2
=
1
cnt1 - cnt2 = 1
cnt1−cnt2=1时,排列方法为偶 奇 偶 奇 ...... 偶
。所以只需要遍历一遍
c
n
t
1
cnt1
cnt1,计算第i个奇数的位置与
(
2
i
−
1
)
(2i-1)
(2i−1)的绝对值之差,最终最少次数就是这些差的和。
4、当
c
n
t
1
=
c
n
t
2
cnt1 = cnt2
cnt1=cnt2时,排列方法为奇 偶 奇 偶 ...... 奇
或偶 奇 偶 奇 ...... 偶
。将两种情况都根据上面的2和3跑一边,更小的那个就是答案。
AC代码
#include <iostream>
#include <cmath>
#include <vector>
using namespace std;
const int N = 1e5+7;
int a[N];
vector<int> v;//用于存储所有奇数的位置
int main(){
int t;
scanf("%d", &t);
while (t--){
v.clear();//清空
int n;
scanf("%d", &n);
int cnt1 = 0, cnt2 = 0;//分别表示奇数和偶数的个数
for (int i = 0; i < n; i++){
int x;
scanf("%d", &x);
if (x % 2 == 1){//该数为奇数
cnt1++;
v.push_back(i);//将位置放入v中
}else{
cnt2++;
}
}
if (abs(cnt1-cnt2) > 1){//当奇数和偶数的个数相差大于1
printf("-1\n");
continue;
}
int len = v.size();
if (cnt1 == cnt2){
int ans1 = 0, ans2 = 0;
for (int i = 0; i < len; i++){
ans1 += abs(v[i] - (2*i));//计算“奇 偶 奇 偶 ... 奇”所需的次数
ans2 += abs(v[i] - (2*i+1));//计算“偶 奇 偶 奇 ... 偶”所需的次数
}
int ans = min(ans1, ans2);//更小的那个为答案
printf("%d\n", ans);
}else if (cnt1 > cnt2){//奇数比偶数多, “奇 偶 奇 偶 ... 奇”
int ans = 0;
for (int i = 0; i < len; i++){
ans += abs(v[i] - (2*i));
}
printf("%d\n", ans);
}else{//偶数比奇数多, “偶 奇 偶 奇 ... 偶”
int ans = 0;
for (int i = 0; i < len; i++){
ans += abs(v[i] - (2*i+1));
}
printf("%d\n", ans);
}
}
return 0;
}
C. Compressed Bracket Sequence
题目: https://codeforces.com/contest/1556/problem/C
题型: 暴力、模拟
题意:
给定一个长度为n的序列,序列的奇数位表示左括号数量,偶数为表右括号数量,求最多有多少个合法的括号子序列。
思路:
先统计出两两配对的答案数,再对剩余的左括号暴力向后找能配对的右括号,还要统计一下以当前括号为起点可以向后连接多少个括号。
AC代码
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 1e4+7;
ll c[N];
int main(){
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++){
scanf("%lld", &c[i]);
}
ll ans = 0;
for (int i = 1; i <= n; i += 2){
ll x = 0, y = 0;
if (i < n){
ans--;
}
for (int j = i+1; j <= n; j += 2){
ans += max(0ll, min(c[i]+y+1, c[j]-x+y+1));
x -= c[j];
y = min(x, y);
x += c[j+1];
}
}
printf("%lld\n", ans);
}
D. Take a Guess
题目: https://codeforces.com/contest/1556/problem/D
题型: 交互、位运算、数学
题意:
交互题,知道一个数组的长度为n、但不知道数组的每一个的值,可以询问不超过2*n次询问,每次询问为:
1、询问and i j
,返回a[i] & a[j]
;
2、询问or i j
,返回a[i] | a[j]
。
求第k大的数字是多少。
思路:
由位运算的知识可知,x&y + x|y = x+y。所以每2次询问同样的i和j便可知道a[i]+a[j]的值。
因此,可以询问and 1 2
和or 1 2
得出sum12,即a[1]+a[2];询问and 2 3
和or 2 3
得出sum23,即a[2]+a[3];询问and 3 1
和or 3 1
得出sum31,即a[3]+a[1]。三元一次方程组求解可得
a
[
1
]
=
s
u
m
12
−
s
u
m
23
+
s
u
m
31
2
a[1] = \frac{sum12-sum23+sum31}{2}
a[1]=2sum12−sum23+sum31、
a
[
2
]
=
s
u
m
23
−
s
u
m
31
+
s
u
m
12
2
a[2] = \frac{sum23-sum31+sum12}{2}
a[2]=2sum23−sum31+sum12、
a
[
3
]
=
s
u
m
31
−
s
u
m
12
+
s
u
m
23
2
a[3] = \frac{sum31-sum12+sum23}{2}
a[3]=2sum31−sum12+sum23。
再接下来2(n-3)次询问,每次询问and 1 i
和or 1 i
得出a[1]+a[i],便可算出a[i]的值。
最后对数组a进行升序排序,a[k]便是所求值。
注意:每次输入询问后要刷新缓冲区,即fflush(stdout)
。
AC代码
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 10007;
ll a[N];
ll ask(int x, int y){
ll u, v;
printf("and %d %d\n", x, y);
fflush(stdout);//刷新缓冲区
scanf("%lld", &u);
printf("or %d %d\n", x, y);
fflush(stdout);//刷新缓冲区
scanf("%lld", &v);
ll ans = u + v;//a+b = a&b + a|b
return ans;//返回a[x]+a[y]
}
int main(){
int n, k;
scanf("%d%d", &n, &k);
int sum12 = ask(1, 2);//a[1]+a[2]
int sum23 = ask(2, 3);//a[2]+a[3]
int sum31 = ask(3, 1);//a[3]+a[1]
a[1] = (sum12-sum23+sum31)/2;
a[2] = (sum23-sum31+sum12)/2;
a[3] = (sum31-sum12+sum23)/2;
for (int i = 4; i <= n; i++){
a[i] = ask(1, i) - a[1];
}
sort(a+1, a+1+n);
printf("finish %lld\n", a[k]);
return 0;
}
*E. Equilibrium
题目: https://codeforces.com/contest/1556/problem/E
待补。。。
*F. Sports Betting
题目: https://codeforces.com/contest/1556/problem/F
待补。。。
*G. Gates to Another World
题目: https://codeforces.com/contest/1556/problem/G
待补。。。