AtCoder Beginner Contest 261 (ABC 261) B~F 题解

B - Tournament Result

题目大意

给你 n n n 支队伍,给出他们的输赢情况,判断是否冲突。

思路

我们设赢为 1 1 1 ,输为 0 0 0 ,平为 − 1 -1 1 ,则当 a i , j + a j , i ≠ 0 a_{i, j} + a_{j, i} \not= 0 ai,j+aj,i=0 时,则存在冲突。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int n, a[1005][1005];
int in() {
    char s = getchar();
    while (s != '-' && s != 'W' && s != 'D' && s != 'L')
        s = getchar();
    if (s == 'W')
        return 1;
    if (s == 'D')
        return 2;
    if (s == 'L')
        return 3;
}
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            a[i][j] = in();
    for (int i = 1; i <= n; i++)
        for (int j = 1; j < i; j++)
            if (a[i][j] + a[j][i] != 4) {
                printf("incorrect");
                return 0;
            }
    printf("correct");
    return 0;
}

C - NewFolder(1)

题目大意

给定 n n n 个字符串,按以下规则输出:

  • 若这个字符串之前每出现过,则输出这个字符串。
  • 若出现过则输出字符串和一个括号,括号里面是之前出现过多少次。

思路

我们用一个 m a p map map 来存就好了。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int n;
string s;
map <string, int> mp;
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        cin >> s;
        cout << s;
        if (mp[s])
            printf("(%d)", mp[s]);
        printf("\n"), mp[s]++;
    }
    return 0;
}

D - Flipping and Bonus

题目大意

n n n 次硬币,如果这次是反面,就将计数器清空,如果这次是正面,就将计数器加一,并且得到 x i x_i xi 元。

m m m 种额外的奖励,当你的计数器为 c i c_i ci 时,你将得到 d i d_i di 的钱。

求你最多能得到多少钱?

思路

考虑 D P DP DP

我们设 f i , j f_{i, j} fi,j 表示当你丢完 i i i 次硬币时,你的计数器的值为 j j j 时最多能得到的钱。

如果 j ≠ 0 j\not=0 j=0,说明这一次丢到的是正面,则 f i , j = f i − 1 , j − 1 + x i + d j f_{i, j} = f_{i - 1, j - 1} + x_i + d_j fi,j=fi1,j1+xi+dj

如果 j = 0 j = 0 j=0,说明这一次丢到的是反面,则 f i , 0 = m a x ( { f i − 1 , j ∣ 0 ≤ j < i } ) f_{i, 0} = max( \left\{ f_{i - 1, j} | 0 \leq j < i \right\} ) fi,0=max({fi1,j∣0j<i})

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int n, m;
LL f[5005][5005], a[5005], b[5005], ans, maxn;
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
        scanf("%lld", &a[i]);
    LL u, v;
    for (int i = 1; i <= m; i++)
        scanf("%lld%lld", &u, &v), b[u] += v;
    f[1][1] = a[1] + b[1];
    for (int i = 2; i <= n; i++) {
        maxn = 0;
        for (int j = 1; j <= i; j++)
            f[i][j] = f[i - 1][j - 1] + a[i] + b[j], maxn = max(f[i - 1][j], maxn);
        f[i][0] = maxn;
    }
    for (int j = 0; j <= n; j++)
        ans = max(ans, f[n][j]);
    printf("%lld", ans);
    return 0;
}

E - Many Operations

题目大意

有一个变量 x x x n n n 种操作,第 i i i 中操作有一对数 T i T_i Ti A i A_i Ai,含义如下:

  • T i = 1 T_i = 1 Ti=1,则 x = x & A i x = x \& A_i x=x&Ai
  • T i = 2 T_i = 2 Ti=2,则 x = x ∣ A i x = x | A_i x=xAi
  • T i = 3 T_i = 3 Ti=3,则 x = x x = x x=x ^ A i A_i Ai

按如下规则进行操作:

  • 执行操作 1 1 1,输出 x x x

  • 执行操作 1 1 1 2 2 2,输出 x x x

  • 执行操作 1 1 1 2 2 2 ⋯ \cdots n n n,输出 x x x

思路

如果把 看作一个数来处理并不是很好处理,我们把 变为二进制数,一位一位看。

还是考虑 D P DP DP ,我们设 f i , j , 0 / 1 f_{i, j, 0/1} fi,j,0/1 表示执行完操作 1 , 2 , ⋯ , i 1,2,\cdots ,i 12i,第 i i i 位原先是 0 / 1 0/1 0/1 ,现在的状态。

于是可以就可以直接按照规则处理。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int n, c, t[200005], a[200005], f[200005][35][2];
int pd(int t, int a, int b) {
    if (t == 1)
        return a & b;
    if (t == 2)
        return a | b;
    return a ^ b;
}
int main() {
    scanf("%d%d", &n, &c);
    for (int i = 1; i <= n; i++)
        scanf("%d%d", &t[i], &a[i]);
    for (int i = 1; i <= 31; i++)
        f[0][i][1] = 1;
    for (int i = 1, op; i <= n; i++) {
        op = a[i];
        for (int j = 1; j <= 30; j++, op = op >> 1) {
            f[i][j][0] = pd(t[i], f[i - 1][j][0], op & 1);
            f[i][j][1] = pd(t[i], f[i - 1][j][1], op & 1);
        }
    }
    int ans = c, last;
    for (int i = 1; i <= n; i++) {
        last = ans, ans = 0;
        for (int j = 1; j <= 30; j++)
            if (f[i][j][(last >> (j - 1)) & 1])
                ans += (1 << (j - 1));
        printf("%d\n", ans);
    }
    return 0;
}

F - Sorting Color Balls

题目大意

n n n 个球,每个球的颜色是 c i c_i ci,球上的数字为 x i x_i xi,我们想要重排这些球使得这些球上的数字从左到右不下降。

我们可以重复以下操作无数次:

  • 选择一个整数 i   ( 1 ≤ i < n ) i\ (1 \leq i < n) i (1i<n),交换第 i i i 个和第 i + 1 i + 1 i+1 个球,如果这两个球颜色相等,则不花钱,如果这两个球颜色不等,则花一块钱。

求所需要的最小花费。

思路

我们知道在不考虑颜色的情况下所需要的最小交换次数就是逆序对的个数。

我们可以用归并求逆序对,在这时多记录以下前面每个颜色有多少个球,在算的时候就可以直接减去相同颜色的球的逆序对数量。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int n, sum[300005];
LL ans;
struct node {
    int color, number;
} a[300005], b[300005];
void guibin(int l, int r) {
    if (l == r)
        return ;
    int mid = (l + r) / 2;
    guibin(l, mid), guibin(mid + 1, r);
    int i = l, j = mid + 1;
    for (int ii = l; ii <= r; ii++)
        sum[a[ii].color] = 0, b[ii] = a[ii];
    for (int ii = l; ii <= mid; ii++)
        sum[a[ii].color]++;
    int k = i;
    while (i <= mid || j <= r) {
        if (i > mid)
            a[k] = b[j], j++;
        else if (j > r)
            a[k] = b[i], i++;
        else {
            if (b[i].number <= b[j].number)
                a[k] = b[i], sum[b[i].color]--, i++;
            else
                ans = ans + mid - i + 1 - sum[b[j].color], a[k] = b[j], j++;
        }
        k++;
    }
}
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i].color);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i].number);
    guibin(1, n);
    printf("%lld", ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值