AtCoder Beginner Contest 367 Review

-1. 闲话

TemplateCoder.

我的理论 perf 没了,不高兴。

不过排得比较靠前(全榜 \(179\),rated \(74\)),比较高兴。

下面开始发饼。

0. 沉浸式体验

20:00:00 正常进入页面,点开第一题。

A. Shout Everyday 什么神金题面,使我大脑旋转

直接改一下 B 让 A 比较容易判断就可以了。

20:01:54 过。

B. Cut .0 什么披着数学皮的字符串处理

直接去除后缀零然后特判一下小数点就可以了。

20:03:24 过。

C. Enumerate Sequences 谁家 ABC 考暴力深搜模拟

直接按照题意模拟即可。

20:06:00 过。

D. Pedometer 啊?环形同余?你在逗我?

直接复制一遍处理前缀和即可。

20:13:12 WA。

于是,尝试把前缀和改成后缀和。

20:22:18 WA。

心态崩了。

去做后面的题。

E. Permute K times 我***

谁家 TemplateCoder 出倍增板子啊?

直接 ST 表处理跳 \(2^i\) 次后的位置即可。

20:28:09 过。

F. Rearrange Query 我***

谁家 TemplateCoder 出哈希板子啊?

直接给每个值赋一个随机值然后做前缀和最后特判一下长度是否相等就可以了。

20:33:04 过。

G. Sum of (XOR^K or 0) 不会,回去看 D。

哦,原来是多算了起点终点相同的情况啊。我***

20:36:40 过。

后面就一无所获了。主要是这次的 G 过于断层。

接下来是正经题解。

1. Shout Everyday

首先,我们需要找到输出 Yes 的充要条件。由于题面并不好看懂,我们可以通过观察样例解释得出,\(B\)\(C\) 描述的是睡觉时间,而 \(A\) 需要在 \([B,C]\) 区间之外。

然而,有时候 \(B>C\)。这个时候,我们可以运用人类智慧,将 \(B\) 减去 \(24\),然后判断即可。

另外值得一提的是,出题人用脚造的数据竟然能让这个错解 AC。

#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int a, b, c;
int main()
{
    scanf("%d%d%d", &a, &b, &c);
    if (c < b)
        b -= 24;
    if (a >= b and a <= c)
        puts("No");
    else
        puts("Yes");
}

2. Cut .0

正如我前面所说的一样,这道题只是一个字符串处理题。

题面中已经讲清楚了,我们需要将 \(X\) 的后缀零清除。并且,如果清除完了小数点后的所有数,小数点也要删掉。

所以照题意模拟即可。

#include <iostream>
#include <string>
using namespace std;
const int N = 1e5 + 10;
string s;
int main()
{
    cin >> s;
    while (s.back() == '0')
        s.pop_back();
    if (s.back() == '.')
        s.pop_back();
    cout << s << '\n';
}

3. Enumerate Sequence

题面也写得非常直白了。就是让你枚举所有可能的序列然后筛选出符合条件的序列输出。

时间复杂度是 \(O(\prod R_i)\),在本题里面最大计算量只到 \(5^8\),完全可以接受。

#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int n, k, r[N], a[N];
void print(int t)
{
    if (t % k)
        return;
    for (int i = 1; i <= n; i++)
    {
        printf("%d%c", a[i], " \n"[i == n]);
    }
}
void dfs(int x, int sm)
{
    if (x > n)
        return print(sm);
    for (int i = 1; i <= r[x]; i++)
    {
        a[x] = i;
        dfs(x + 1, sm + i);
    }
}
int main()
{
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", r + i);
    }
    dfs(1, 0);
}

4. Pedometer

就是这道题让我失去了理论 perf 值。

首先,在没有环的情况下,有一个非常显然的模前缀和做法,直接开桶计数即可。

如果有环,就将数组复制两份,在里面做长度为 \(n-1\) 的滑动窗口就可以了。注意,一定是 \(n-1\),否则起点终点相同的情况会被统计,然后 WA 掉。

时间复杂度 \(O(n)\)

#include <iostream>
#include <map>
using namespace std;
const int N = 2e5 + 10, M = 1e6 + 10;
int n, m, cnt[M];
using ll = long long;
ll a[N << 1], res;
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
    {
        scanf("%lld", a + i);
        a[n + i] = a[i];
    }
    for (int i = n * 2; i > n; i--)
    {
        a[i] = (a[i] + a[i + 1]) % m;
        cnt[a[i]]++;
    }
    for (int i = n; i; i--)
    {
        cnt[a[n + i]]--;
        a[i] = (a[i] + a[i + 1]) % m;
        res += cnt[a[i]];
        cnt[a[i]]++;
    }
    // for (int i = 1; i <= 2 * n; i++)
    // {
    //     printf("%lld%c", a[i], " \n"[i == 2 * n]);
    // }
    printf("%lld\n", res);
}

5. Permute K times

看到题目,发现有数字转移,再发现 \(0\le K\le 10^{18}\),果断打 ST 表。

具体的,记 \(x_{i,j}\) 为经过 \(2^j\) 次转移后 \(x_i\) 的值。

显然,\(x_{i,j}=x_{x_{i,j-1},j-1}\)

按照此方法转移即可。

时间复杂度 \(O(N\log K)\)

#include <iostream>
using namespace std;
const int N = 2e5 + 10;
int n, x[N][64], a[N];
using ll = long long;
ll k;
inline int find(int tx, ll k)
{
    for (int i = 0; k; i++, (k >>= 1))
    {
        if (k & 1)
            tx = x[tx][i];
    }
    return tx;
}
int main()
{
    scanf("%d%lld", &n, &k);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", x[i]);
    }
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", a + i);
    }
    for (int k = 1; k < 64; k++)
    {
        for (int i = 1; i <= n; i++)
        {
            x[i][k] = x[x[i][k - 1]][k - 1];
        }
    }
    for (int i = 1; i <= n; i++)
    {
        printf("%d%c", a[find(i, k)], " \n"[i == n]);
    }
}

6. Rearrange Query

看到区间排序后相同,果断转化为区间所代表的多重集相等,于是哈希。

这里,仍然要强烈推荐一种非常好写的随机算法。

定义一个梅森旋转器,用 random_device 初始化,在每个值上调用一次梅森旋转。这样既好写又不用担心固定 base 被卡。

时间复杂度 \(O(n+q)\)

#include <iostream>
#include <random>
using namespace std;
const int N = 2e5 + 10;
int n, q, a[N], b[N];
using ull = unsigned long long;
random_device rd;
mt19937 mt(rd());
ull hs[N], hspa[N], hspb[N];
int main()
{
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; i++)
        hs[i] = mt();
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", a + i);
        hspa[i] = hspa[i - 1] + hs[a[i]];
    }
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", b + i);
        hspb[i] = hspb[i - 1] + hs[b[i]];
    }
    for (int i = 1, ta, tb, tc, td; i <= q; i++)
    {
        scanf("%d%d%d%d", &ta, &tb, &tc, &td);
        if (tb - ta + 1 != td - tc + 1)
        {
            puts("No");
            continue;
        }
        if (hspa[tb] - hspa[ta - 1] != hspb[td] - hspb[tc - 1])
        {
            puts("No");
            continue;
        }
        puts("Yes");
    }
}
posted @ 2024-08-17 23:49  丝羽绫华  阅读(260)  评论(2)    收藏  举报