比赛链接
A. Yellow Cards
题意:你有n张黄牌,有两只球队分别有a1,a2个人,两个球队的人分别吃k1,k2张黄牌就会被罚下场。问最多,最少可以使多少人被罚下场。
题解:贪心。让最多的人罚下场,先贪心选能吃黄牌少的人。让最少的人罚下场,就给每个人都分配k - 1张黄牌,多出的黄牌就是要罚出的人。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a1, a2, k1, k2, n;
scanf("%d%d%d%d%d", &a1, &a2, &k1, &k2, &n);
int tmp1 = (k1 - 1) * a1 + (k2 - 1) * a2;
int ans1 = 0, ans2 = 0;
if(n > tmp1)
ans1 = n - tmp1;
if(k1 < k2){
swap(k1,k2);
swap(a1,a2);
}
if(n >= k2 * a2){
n -= k2 * a2;
ans2 += a2;
}
else {
ans2 += n / k2;
n = 0;
}
ans2 += n / k1;
cout<<ans1<<' '<<ans2<<endl;
}
B. The Number of Products
题意:给你一个序列,问有多少个子区间乘积是正数,多少个是负数。
题解:简单区间问题一般涉及到前缀和知识。我们可以求一个前缀乘积,统计此时正数与负数的个数,然后开始遍历数组,遇到正数就正数的个数减1,遇到负数就负数的个数减1,然后因为少乘了一个负数,正负交换一下。实际就是遍历统计以当前位置为起始点到n的子区间的正负个数。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int a[N];
int main()
{
int n;
cin>>n;
int p1 = 0, p2 = 0, tmp = 1;
for(int i = 1; i <= n; ++i){
cin >> a[i];
if(a[i] < 0)
a[i] = -1;
else
a[i] = 1;
tmp *= a[i];
if(tmp > 0)
p1++;
else
p2++;
}
long long ans1 = 0, ans2 = 0, flg = 1;
for(int i = 1; i <= n; ++i){
ans1 += p1;
ans2 += p2;
if(a[i] == 1)
p1 -= 1;
else {
p2 -= 1;
swap(p1, p2);
}
}
cout << ans2 <<' '<<ans1<<endl;
}
C. Swap Letters
题意:有两个串 s ,t 只包含' a ' ,' b '字符,你可以交换 s 串和 t 串 任意位置的字符。问最少的交换次数,使s串与t串相等。
题解:首先可以发现两个串只有aa,ab,ba,bb四种情况,为了使s与t串相等,我们需要每次交换两个 ab 或者 ba,所以只能是偶数个ab,ba。统计ab与ba的个数,当(ba + ab)为奇数时无解。当ba 以及 ab 个数都为奇数个时,将一个 ba 变为 ab ,这样就都是偶数个了。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 100;
char s[N], t[N];
int vis[N];
struct node{
int l, r;
}ans[N];
int main()
{
int n;
cin>>n;
cin>>s + 1>>t + 1;
int ab =0, ba = 0;
for(int i = 1; i <= n; ++i){
if(s[i] == 'a' && t[i] == 'b') ab++, vis[i] = 1;
if(s[i] == 'b' && t[i] == 'a') ba++, vis[i] = 2;
}
if((ab + ba) % 2 == 1)
return puts("-1");
int cnt = 0;
if(ab%2 == 1){
for(int i = 1; i <= n; ++i){
if(vis[i] == 1){
vis[i] = 2;
ans[++cnt] = node{i, i};
break;
}
}
}
int pre = 0;
for(int i = 1; i <= n; i++){
if(vis[i] == 1){
if(!pre)
pre = i;
else {
ans[++cnt] = node{pre, i};
pre = 0;
}
}
}
pre = 0;
for(int i = 1; i <= n; i++){
if(vis[i] == 2){
if(!pre)
pre = i;
else {
ans[++cnt] = node{pre, i};
pre = 0;
}
}
}
cout<<cnt<<endl;
for(int i = 1; i <= cnt; i++)
printf("%d %d\n",ans[i].l, ans[i].r);
}
D. Ticket Game
题意:给你一个串,包含0~9的数字以及?,?可以由两个玩家轮流填数,填数范围也是0~9。当序列中没有?时游戏结束,如果此时序列前半部分的和等于后半部分和,B赢否则M赢。
题解:统计前半部分以及后半部分和与?数量记为c1,c2,p1,p2,分四种情况:
- c1 == c2 p1 == p2 B赢,无论M怎么填数,B都可以在另一边填相同的数
- c1 != c2 p1 == p2 M赢,M在总和大的那一边一直填9就能赢了
- c1 == c2 p1 != p2 M赢,M在?多的那一边一直填9就赢了
- c1 != c2 p1 != p2 再分情况 , 假设c1 > c2
- 如果p1 > p2 M 赢 , 在前半部分 一直填 9 就好了
- 如果 p1 < p2 : p2 先 剪掉 p1 ,因为无论M怎么填,B都可以在另一边填相同的。然后再判断(c1 - c2)是否等于 p2 * 9
- 如果小于 ,M一直填0 ,M 赢。如果大于 M 一直填9,M赢。只有等于的时候,无论M怎么填,B都可以选出一个数与M凑成9,B赢。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
char s[N];
int main()
{
int n;
cin>>n;
cin>>s + 1;
int c1 = 0, c2 = 0, p1 = 0, p2 = 0;
for(int i = 1; i <= n; ++i){
if(i <= n / 2){
if(s[i] == '?') p1++;
if(s[i] >= '0' && s[i] <= '9')
c1 += s[i] - '0';
}
else {
if(s[i] == '?') p2++;
if(s[i] >= '0' && s[i] <= '9')
c2 += s[i] - '0';
}
}
if(c1 == c2 && p1 == p2) puts("Bicarp");
else if(c1 != c2 && p1 == p2) puts("Monocarp");
else if(c1 == c2 && p1 != p2) puts("Monocarp");
else {
if(c1 < c2) {
swap(c1, c2);
swap(p1, p2);
}
if(p1 > p2) puts("Monocarp");
else {
p2 -= p1;
if((p2 / 2) * 9 > c1 - c2 || (p2 / 2) * 9 < c1 -c2) puts("Monocarp");
else puts("Bicarp");
}
}
}
E题加强了数据,一般做法是错的,一些AC代码过不了加强的数据,贴一下错误代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[25][25], cnt[2], ans;
int main()
{
int n, x;
cin>>n;
for(int i = 1; i <= n; ++i){
cin>>x;
cnt[x]++;
for(int j = 1; j <= 20; ++j)
if(j != x)
dp[x][j] += cnt[j];
}
for(int i = 1; i <= 20; ++i)
for(int j = 1; j <= 20; ++j)
if(i != j )
ans += min(dp[i][j], dp[j][i]);
cout<<ans/2<<endl;
}