Educational Codeforces Round 113 (Rated for Div. 2) 题解

8 篇文章 0 订阅
8 篇文章 0 订阅

旅行传送门

一些闲话:打得稀碎,不知道为什么昨晚状态差的一逼,20分钟应该切出来的题硬生生看了两小时,难受到一宿都没睡好,做梦都在想题,所幸今天早上起来状态好点了,补题的时候不至于像昨晚那样没思路了,虽然掉大分还是很心痛(っ╥╯﹏╰╥c)

A. Balanced Substring

题意:给你一个仅由字母 a a a b b b 组成的字符串 s s s ,要你找到某个区间,使得区间内字母 a a a b b b 的个数相等。

题目分析:签到题切了20分钟,一开始还准备拿双指针去瞎搞,我都不知道自己在想些啥。

考虑最简单的情况,只需要找是否存在子串 a b ab ab b a ba ba 就好了,如果没有,那么这样的区间可以证明一定不存在。

AC代码

//代码早上起来重修的,昨晚写的不知道是什么sh*t
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
#define pii pair<int, int>
#define mp make_pair
using namespace std;

pii solve()
{
    int n;
    cin >> n;
    string s;
    cin >> s;
    pii res = mp(-1, -1);
    string m1 = "ab", m2 = "ba";
    int pos = s.find(m1);
    if (pos != s.npos)
    {
        res.first = pos + 1, res.second = pos + 2;
        return res;
    }
    pos = s.find(m2);
    if (pos != s.npos)
    {
        res.first = pos + 1, res.second = pos + 2;
        return res;
    }
    return res;
}

int main(int argc, char const *argv[])
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--)
    {
        pii ans = solve();
        printf("%d %d\n", ans.first, ans.second);
    }
    return 0;
}

B. Chess Tournament

题意 n n n 位选手参加比赛。两两battle一场。 每场比赛的结果要么是一方胜一方负,要么是双方平局。

每位选手都有自己的期望,他们是以下两者之一:

  • 选手不希望输掉任何一场比赛
  • 选手想要至少赢得一场比赛

问是否可以满足所有选手的期望,若可以则输出比赛结果。

题目分析:我是傻逼,简简单单的签到题硬是先写加了一大堆if else的sh*t mountain代码wa了整整两发,之后又写了份晦涩难懂的代码a了,被自己整无语了(流汗黄豆)。

先考虑什么情况下不能满足,若拥有第二种期望(不爆零)的选手数量为 1 1 1 2 2 2 ,则输出 N O NO NO 。很好理解,在此不过多赘述。

之后扫一遍整个字符串,假设第 i i i 位选手期望为 1 1 1 ,就把第 i i i i i i 列都划上等号(视为平局),否则就在当前行第一个空位赢一把,之后的都输掉就行了。

AC代码

#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
const int maxn = 55;

char s[maxn];
int mp[maxn][maxn];
int main(int argc, char const *argv[])
{
    int t;
    scanf("%d", &t);
    while (t--)
    {
        memset(mp, 0, sizeof(mp));
        int n;
        scanf("%d", &n);
        scanf("%s", s + 1);
        int n1 = 0, n2 = 0;
        rep(i, 1, n) s[i] == '1' ? ++n1 : ++n2;
        if (n1 == n)
        {
            puts("YES");
            rep(i, 1, n)
            {
                rep(j, 1, n) i == j ? printf("X") : printf("=");
                puts("");
            }
            continue;
        }
        if (n2 == 1 || n2 == 2)
        {
            puts("NO");
            continue;
        }
        rep(i, 1, n) if (s[i] == '1') rep(j, 1, n) mp[i][j] = mp[j][i] = 3;
        rep(i, 1, n) rep(j, 1, n) if (i == j) mp[i][j] = 4;
        rep(i, 1, n)
        {
            if (s[i] == '2')
            {
                int flag = 0;
                rep(j, 1, n)
                {
                    if (flag)
                        mp[i][j] = 2, mp[j][i] = 1;
                    if (!mp[i][j])
                        mp[i][j] = 1, mp[j][i] = 2, flag = 1;
                }
            }
        }
        puts("YES");
        rep(i, 1, n)
        {
            rep(j, 1, n)
            {
                if (mp[i][j] == 1)
                    printf("+");
                else if (mp[i][j] == 2)
                    printf("-");
                else if (mp[i][j] == 3)
                    printf("=");
                else if (mp[i][j] == 4)
                    printf("X");
            }
            puts("");
        }
    }
    return 0;
}

C. Jury Meeting

题意: 给你一个序列 a a a ,让你确定一个顺序,按这个顺序依次递减(已经为 0 0 0 的元素则跳过)直至序列元素全为 0 0 0 为止,递减过程中,若没有一个元素连续减两次,则称其为“好顺序”,问共有多少个好顺序?

举栗子:

假设序列 a a a 中包含元素 1 、 2 、 3 1、2、3 123

1 、 3 、 2 1、3、2 132 排序: [ 1 , 3 , 2 ] [1,3,2] [1,3,2] -> [ 0 , 2 , 1 ] [0,2,1] [0,2,1] -> [ 0 , 1 , 0 ] [0,1,0] [0,1,0] -> [ 0 , 0 , 0 ] [0,0,0] [0,0,0] 好顺序

1 、 2 、 3 1、2、3 123 排序: [ 1 , 2 , 3 ] [1,2,3] [1,2,3] -> [ 0 , 1 , 2 ] [0,1,2] [0,1,2] -> [ 0 , 0 , 1 ] [0,0,1] [0,0,1] -> [ 0 , 0 , 0 ] [0,0,0] [0,0,0] 坏顺序( a 3 a_3 a3 被不间断地连续减了两次)

题目分析:先分三种情况讨论吧:

  • 记最大值为 m a x max max ,若 m a x max max 的个数 ≥ 2 \geq 2 2 ,显然不管按什么顺序递减都是好顺序,此时 a n s ans ans A n n A_{n}^{n} Ann
  • 记最大值为 m a x max max ,次大值为 c m a x cmax cmax ,若两者差值 ≥ 2 \geq 2 2 ,显然不管按什么顺序递减都是坏顺序,因为其它值都减为 0 0 0 的时候最大值所余留的值必定 ≥ 2 \geq 2 2 ,必定会被不间断地连续减两次,此时 a n s ans ans 0 0 0
  • 排除以上两种情况后,设元素个数为 n n n ,其它值(除最大值和次大值以外的值)的个数为 t t t ,最初 a n s ans ans 为总排列 A n n A_{n}^{n} Ann ,最大值处于序列末尾时,无论前面怎么排都是坏顺序, a n s ans ans 先减去 A n − 1 n − 1 A_{n-1}^{n-1} An1n1
  • 之后最大值从末尾依次向前推进,后面随之留出空位,我们记空位数为 m m m ,那么 m m m 循环从 [ 1 1 1 -> t t t] , a n s ans ans 每次减去 C t m × m ! × ( n − m − 1 ) ! C_{t}^{m} \times m! \times (n-m-1)! Ctm×m!×(nm1)! ,最终结果即为所求, C t m C_{t}^{m} Ctm 是从 t t t 个其它值中选出 m m m 个填入空位,最大值前后的排序方案数分别为 m ! m! m! ( n − m − 1 ) ! (n-m-1)! (nm1)!
  • 还是举个栗子说人话,比如序列包含元素 1 、 2 、 3 、 4 、 5 1、2、3、4、5 12345 ,一开始 a n s = 120 ans = 120 ans=120 ,减去 5 5 5 在末尾的方案数后 a n s = 96 ans = 96 ans=96 ,然后 5 5 5 向前推进一位,有 [ _ , _ , _ , 5 5 5 , _ ] ,次大值 4 4 4 必须放在 5 5 5 前面(这时才能构成坏顺序),那么 a n s ans ans 就要再减去 C 3 1 × 1 ! × A 3 3 C_{3}^{1} \times 1! \times A_{3}^{3} C31×1!×A33 ,这样做下去最终 a n s ans ans 60 60 60

AC代码

#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
using ll = long long;
const int mod = 998244353;
const int maxn = 2e5 + 5;

char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
inline ll read()
{
    ll x = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch))
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}

ll t, fac[maxn], inv[maxn], a[maxn];

ll cal(ll t, ll m)
{
    ll res = 1;
    rep(i, 1, m) res = (res % mod * (t - m + i) % mod) % mod;
    return res;
}

ll fpow_mod(ll a, ll b)
{
    ll ans = 1;
    while (b)
    {
        if (b & 1)
            ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}

ll getC(ll n, ll m) //C(n,m) = n!/((n-m)!*m!) % mod
{
    return fac[n] * inv[n - m] % mod * inv[m] % mod;
}

ll solve()
{
    ll n = read();
    fac[0] = inv[0] = 1;
    rep(i, 1, n)
    {
        fac[i] = 1ll * fac[i - 1] * i % mod;
        inv[i] = fpow_mod(fac[i], mod - 2);
    }
    ll mx = 0, cnt = 0;
    rep(i, 1, n)
    {
        a[i] = read();
        if (a[i] > mx)
            mx = a[i], cnt = 1;
        else if (a[i] == mx)
            ++cnt;
    }
    if (cnt > 1)
        return fac[n];
    std::sort(a + 1, a + n + 1);
    cnt = 0;
    if (a[n] - a[n - 1] > 1)
        return 0;
    rep(i, 1, n) if (a[i] == a[n - 1])++ cnt;
    ll others = n - cnt - 1;
    ll res = fac[n];
    res = (res - fac[n - 1] + mod) % mod;
    rep(i, 1, others)
    {
        res = (res - ((getC(others, i) * fac[i]) % mod * fac[n - i - 1]) % mod + mod) % mod;
    }
    return res;
}

int main(int argc, char const *argv[])
{
    t = read();
    while (t--)
        printf("%lld\n", solve() % mod);
    return 0;
}

你以为就这样结束了?

没有

其实有更简单的做法

早上和大哥聊起这个题时,大哥听了我的思路,很是疑惑不解:为什么不直接算答案?

假设次大值个数为 k k k ,最大值和次大值一共有 k + 1 k+1 k+1 种位置关系,而只有一种关系不合法,所以答案就是 n ! × k k + 1 n! \times \frac{k}{k+1} n!×k+1k

嗯,我果然傻逼。

AC代码

#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
using ll = long long;
const int mod = 998244353;
const int maxn = 2e5 + 5;

char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
inline ll read()
{
    ll x = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch))
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}

ll t, fac[maxn], inv[maxn], a[maxn];

ll cal(ll t, ll m)
{
    ll res = 1;
    rep(i, 1, m) res = (res % mod * (t - m + i) % mod) % mod;
    return res;
}

ll fpow_mod(ll a, ll b)
{
    ll ans = 1;
    while (b)
    {
        if (b & 1)
            ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}

ll divi(ll a, ll b)
{
    b = fpow_mod(b, mod - 2); //b的逆元
    return a * b % mod;
}

ll solve()
{
    ll n = read();
    fac[0] = inv[0] = 1;
    rep(i, 1, n)
    {
        fac[i] = 1ll * fac[i - 1] * i % mod;
        inv[i] = fpow_mod(fac[i], mod - 2);
    }
    ll mx = 0, cnt = 0;
    rep(i, 1, n)
    {
        a[i] = read();
        if (a[i] > mx)
            mx = a[i], cnt = 1;
        else if (a[i] == mx)
            ++cnt;
    }
    if (cnt > 1)
        return fac[n];
    std::sort(a + 1, a + n + 1);
    cnt = 0;
    if (a[n] - a[n - 1] > 1)
        return 0;
    rep(i, 1, n) if (a[i] == a[n - 1])++ cnt;
    ll res = fac[n] * divi(cnt, cnt + 1) % mod;
    return res;
}

int main(int argc, char const *argv[])
{
    t = read();
    while (t--)
        printf("%lld\n", solve());
    return 0;
}

D. Inconvenient Pairs

题意:在一个直角坐标系中,给你一些水平线和竖直线,还有一些在直线上的点,点只能在线上移动。问你有多少个点对满足它们之间的距离大于它们的曼哈顿距离?

题目分析:我们不妨将点分为三种类型:

  • 只在水平线上
  • 只在竖直线上
  • 在水平线和竖直线的交汇处

显然第三种点到其它点的距离都等于曼哈顿距离,而对于前两种情况的点,不难发现在水平线上的点与竖直线上的点之间的距离也一定是曼哈顿距离,所以有贡献的点对只会出现在两个点都是情况一或情况二上,在此不给出证明了(因为我也证不出),画下图应该很好理解。

那什么条件下,在同一种类型的直线上的两点会对答案有贡献呢,通过观察可以发现,若两点间不存在另一种类型的直线,那一个点就必须得“绕远路”才能走到另一个点,这个时候的点对即为合法的!说人话就是,比如样例中的点 3 3 3 与点 4 4 4 5 5 5 就是,这三个点都在竖直线上,但它们之间都没有“夹着”一条水平线,因此任两点都能作出贡献,而点 5 5 5 与点 8 8 8 之间就夹了一条 y = 4 y = 4 y=4 的水平直线,所以这两个点就没贡献。

那么我们的任务就转换成如何统计这些点对了,首先我们把这三种类型的点分出来,一开始我想的是用树状数组,在竖直线上点的插水平线的值统计,水平线上的插竖直线统计,但是时间复杂度还是太高了,后来看了看其它julao的题解,直接枚举直线,统计相邻两直线间夹的点,这里给个旅行传送门

稍微解释下julao的博客,如图:

hOQZAx.png

现在这两条直线间存在 6 6 6 个点,所以我们用 m a p map map 记录下每个点所在的直线坐标,这样就解决了多个点在同一直线的问题(因为在同一直线上的两点是不会有贡献的),那么这次更新的话新增的贡献即为: 1 × 5 + 2 × 3 1 \times 5 + 2 \times 3 1×5+2×3

AC代码

#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
#define pii std::pair<int, int>
using ll = long long;
const int maxn = 3e5 + 5;

char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch))
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}

std::vector<int> vs(maxn), hs(maxn), vx(maxn), vy(maxn);
std::vector<pii> ps(maxn);

inline bool cmp1(int a, int b) { return ps[a].second < ps[b].second; }
inline bool cmp2(int a, int b) { return ps[a].first < ps[b].first; }

ll solve()
{
    ll res = 0;
    int n = read(), m = read(), k = read();
    std::map<int, int> mpx, mpy, mp;
    rep(i, 1, n)
    {
        vs[i] = read();
        ++mpx[vs[i]];
    }
    rep(i, 1, m)
    {
        hs[i] = read();
        ++mpy[hs[i]];
    }
    int n1 = 0, n2 = 0;
    rep(i, 1, k)
    {
        int x = read(), y = read();
        ps[i] = std::make_pair(x, y);
        if (mpx[x] && !mpy[y])
            vx[++n1] = i;
        if (!mpx[x] && mpy[y])
            vy[++n2] = i;
    }
    std::sort(vx.begin() + 1, vx.begin() + n1 + 1, cmp1);
    std::sort(vy.begin() + 1, vy.begin() + n2 + 1, cmp2);
    int num = 1;
    rep(i, 1, n - 1)
    {
        int point = 0;
        while (num <= n2 && ps[vy[num]].first > vs[i] && ps[vy[num]].first < vs[i + 1])
        {
            ++mp[ps[vy[num]].second];
            ++point;
            ++num;
        }
        for (auto it : mp)
        {
            point -= it.second;
            res += 1ll * point * it.second;
        }
        mp.clear();
    }
    num = 1;
    rep(i, 1, m - 1)
    {
        int point = 0;
        while (num <= n1 && ps[vx[num]].second > hs[i] && ps[vx[num]].second < hs[i + 1])
        {
            ++mp[ps[vx[num]].first];
            ++point;
            ++num;
        }
        for (auto it : mp)
        {
            point -= it.second;
            res += 1ll * point * it.second;
        }
        mp.clear();
    }
    return res;
}

int main(int argc, char const *argv[])
{
    int t = read();
    while (t--)
        printf("%lld\n", solve());
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
"educational codeforces round 103 (rated for div. 2)"是一个Codeforces平台上的教育性比赛,专为2级选手设计评级。以下是有关该比赛的回答。 "educational codeforces round 103 (rated for div. 2)"是一场Codeforces平台上的教育性比赛。Codeforces是一个为程序员提供竞赛和评级的在线平台。这场比赛是专为2级选手设计的,这意味着它适合那些在算法和数据结构方面已经积累了一定经验的选手参与。 与其他Codeforces比赛一样,这场比赛将由多个问题组成,选手需要根据给定的问题描述和测试用例,编写程序来解决这些问题。比赛的时限通常有两到三个小时,选手需要在规定的时间内提交他们的解答。他们的程序将在Codeforces的在线评测系统上运行,并根据程序的正确性和效率进行评分。 该比赛被称为"educational",意味着比赛的目的是教育性的,而不是针对专业的竞争性。这种教育性比赛为选手提供了一个学习和提高他们编程技能的机会。即使选手没有在比赛中获得很高的排名,他们也可以从其他选手的解决方案中学习,并通过参与讨论获得更多的知识。 参加"educational codeforces round 103 (rated for div. 2)"对于2级选手来说是很有意义的。他们可以通过解决难度适中的问题来测试和巩固他们的算法和编程技巧。另外,这种比赛对于提高解决问题能力,锻炼思维和提高团队合作能力也是非常有帮助的。 总的来说,"educational codeforces round 103 (rated for div. 2)"是一场为2级选手设计的教育性比赛,旨在提高他们的编程技能和算法能力。参与这样的比赛可以为选手提供学习和进步的机会,同时也促进了编程社区的交流与合作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值