AtCoder Regular Contest 119 解题报告

本文同步发表在 YangTY’s Blog

ARC119A - 119 × 2^23 + 1

给定 n n n 1 ≤ n ≤ 1 × 1 0 18 1\le n\le 1\times 10^{18} 1n1×1018),将 n n n 分解为 a × 2 b + c a \times 2^b + c a×2b+c 的形式,求 a + b + c a + b + c a+b+c 的最小值。

b b b 0 0 0 61 61 61 枚举即可。

ARC119B - Electric Board

Description

给定两个 01 串 S S S T T T,每次对 S S S 的操作可以选取 ( l , r ) (l,r) (l,r),满足

  • S l = 0 ∧ S l + 1 = S l + 2 = ⋯ = S r = 1 S_l = 0\land S_{l+ 1} = S_{l + 2} = \cdots = S_{r} = 1 Sl=0Sl+1=Sl+2==Sr=1
  • S l = S l + 1 = ⋯ = S r − 1 = 1 ∧ S r = 0 S_l = S_{l + 1} = \cdots = S_{r - 1} = 1 \land S_r = 0 Sl=Sl+1==Sr1=1Sr=0

然后交换 S l S_l Sl S r S_r Sr。问最少多少次操作之后可以将 S S S 变成 T T T

Solution

我们不妨将每次操作看为 0 0 0 的移动操作,因为 1 1 1 是连续动的,不好考虑。那么就只需要从左往右进行贪心,把 S S S 的每个 0 0 0 依次往右移直到与 T T T 0 0 0 对应。记录下每个 0 0 0 的位置然后直接贪心就可以了。

#include <cstdio>
#include <cstring>
#define FOR(i, a, b) for (int i = a; i <= b; ++i)
#define DEC(i, a, b) for (int i = a; i >= b; --i)

template<typename T> inline void swap(T &a,T &b)
{
    T t = a;
    return void(a = b, b = t);
}

const int maxn = 5e5 + 5;

int n;
char s[maxn], t[maxn];
int pos1[maxn], pos2[maxn];

int main()
{
    scanf("%d", &n);
    scanf("%s", s + 1);
    scanf("%s", t + 1);
    FOR(i, 1, n) if (s[i] == '0') pos1[++pos1[0]] = i;
    FOR(i, 1, n) if (t[i] == '0') pos2[++pos2[0]] = i;
    int ans = 0;
    if (pos1[0] != pos2[0])
        return puts("-1"), 0;
    for (int i = 1, j = 1; i <= pos2[0]; ++i, ++j)
    {
        if (j > pos1[0])
            return puts("-1"), 0;
        ++ans;
        if (pos1[j] == pos2[i]) --ans;
    }
    printf("%d\n", ans);
    return 0;

}

ARC119C - ARC Wrecker 2

Description

给定长度为 n n n 的序列 A i A_i Ai,对于区间 [ l , r ] [l,r] [l,r],每次可选择 [ l , r − 1 ] [l, r - 1] [l,r1] 间的一个整数 x x x,然后将 A x A_x Ax A x + 1 A_{x + 1} Ax+1 同时加一或减一。问有多少个 [ l , r ] [l,r ] [l,r] 可以在有限次操作之后变为全 0 0 0

Solution

比较妙的思路。

每次操作相当于是给奇数下标的数和偶数下标的数同时加减 1 1 1,所以为了最后得到全 0 0 0,一开始选的区间中奇下标和偶下标数的和必须相等。

注意到这条性质之后记录一下前缀和,然后用 map 随便记录以下就可以做了。

#include <cstdio>
#include <cctype>
#include <map>
#define FOR(i, a, b) for (int i = a; i <= b; ++i)
#define DEC(i, a, b) for (int i = a; i >= b; --i)

const int maxn = 3e5 + 5;

int read()
{
    int s = 0, x = 0;
    char c = getchar();
    while (!isdigit(c))
        x |= (c == '-'), c = getchar();
    while (isdigit(c))
        s = s * 10 + c - '0', c = getchar();
    return x ? -s : s;
}

typedef long long ll;

int n;

ll ans, a[maxn], s[maxn];

std::map<ll, ll> m;

int main()
{
    n = read();
    FOR(i, 1, n) a[i] = read() * ((i & 1) ? -1 : 1);
    FOR(i, 1, n) s[i] = a[i] + s[i - 1], ++m[s[i]];
    ++m[0];
    for (std::map<ll, ll>::iterator it = m.begin(); it != m.end(); ++it)
        ans += (*it).second * ((*it).second - 1ll) / 2;
    printf("%lld\n", ans);
    return 0;
}

答案的上界是会爆 int,要开 long long

ARC119D - Grid Repainting 3

Description

给定一 H H H W W W 列的方格,每个方格一开始涂了红色或者蓝色。进行如下操作:对于当前的每个红色格子,可以选择它并将其所在的一行或一列全部涂满白色。问最后最多多少个格子可以是白色并构造方案。

Solution

对于这种方格并且操作是对于整行或整列的题,很容易想到构建一张二分图来解决问题。左部的点代表每行,右部的点代表每列,一个红格子代表一条边。

然后我们分析这个涂色方案。涂掉一整行/列相当于废掉这一个点,同时把这个点相关的所有边全部废掉(即为废掉在这行/列上的红格子)。当然废掉这个点的条件是它能引出至少一条边(即必须有至少一个红格子在这行/列上面)。依据这个想法,我们肯定希望一个连通分量内,废掉的点(即涂掉的行/列)越多越好,考虑如何使它最多。

注意到,一个连通分量删到最后一定会剩下一个点(至于为什么可以自己思考一下)。我们尝试构造一下方案:假设我们剩点 u u u,那么就可以从 u u u 构建这个连通分量的 dfs 生成树,然后从叶子节点往上删点,这样一定可以保证是合法而且除了 u u u,其他的点会被删干净。

一张图是有很多个连通分量的,而我们需要对每个连通分量进行决策:留下哪个点最优。我们要涂白的格子最多,假设涂了 r r r c c c 列,则剩下的蓝格子为 ( h − r ) ( w − c ) (h - r)(w - c) (hr)(wc) 个,我们需要将其最小化。不难发现我们可以直接枚举留下来的行点和列点的个数,然后找到最优解就直接决策每个连通分量剩下行还是列(反正哪一行哪一列不重要),这题就做完了。

主要的流程:

  • 染色标记连通分量
  • 决策剩下多少列点和多少行点
  • dfs 记录答案
  • 输出

Code

#include <cstdio>
#include <cstring>
#define FOR(i, a, b) for (int i = a; i <= b; ++i)
#define GO(u) for (int i = head[u], v = to[i]; i; i = nxt[i], v = to[i])

const int maxn = 2500 + 5, maxm = 2 * maxn * maxn + 5;

int h, w;

int head[maxn << 1], to[maxm], nxt[maxm], cnte;

void add(int u, int v)
{
    to[++cnte] = v;
    nxt[cnte] = head[u];
    head[u] = cnte;
    return;
}

int bel[maxn << 1], vis[maxn << 1], L[maxn << 1], R[maxn << 1];

void color(int u, int fa, int c)
{
    if (bel[u]) return;
    bel[u] = c;
    u > h ? ++R[c] : ++L[c];
    GO(u) if (v != fa) color(v, u, c);
    return;
}

int calced[maxn];

char type[maxm];
int X[maxm], Y[maxm], ans;

void dfs(int u, int fa)
{
    vis[u] = 1;
    GO(u)
    {
        if (vis[v] || v == fa) continue;
        dfs(v, u);
        ++ans;
        type[ans] = v > h ? 'Y' : 'X';
        X[ans] = u > v ? v : u;
        Y[ans] = u > v ? u - h : v - h;
    }
    return;
}

int main()
{
    scanf("%d %d", &h, &w);
    FOR(i, 1, h)
    {
        char s[maxn];
        scanf("%s", s + 1);
        FOR(j, 1, w)
        {
            if (s[j] == 'R')
                add(i, j + h), add(j + h, i);
        }
    }
    int cccnt = 0, sumr = 0, sumc = 0;
    FOR(i, 1, h)
    {
        if (bel[i]) continue;
        if (!to[head[i]]) continue;
        else color(i, 0, ++cccnt), sumr += L[cccnt], sumc += R[cccnt];
    }
    int mintmp = h * w, resr, resc;
    for (int r = 0, c = cccnt; r <= cccnt; ++r, --c)
        if ((h - sumr + r) * (w - sumc + c) < mintmp)
            resr = r, resc = c, mintmp = (h - sumr + r) * (w - sumc + c);
    FOR(i, 1, h + w)
    {
        if (L[bel[i]] + R[bel[i]] <= 1 || calced[bel[i]]) continue;
        if (resr && i <= h) dfs(i, 0), resr--, calced[bel[i]] = 1;
        else if (resc && i > h) dfs(i, 0), resc--, calced[bel[i]] = 1; 
    }
    printf("%d\n", ans);
    FOR(i, 1, ans) printf("%c %d %d\n", type[i], X[i], Y[i]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值