AtCoder Beginner Contest 223
阅读原文时间:2023年07月09日阅读:3
  • A是纯纯的水题,就不说了

B - String Shifting

思路分析

  • 我真的sb,一开始想了好久是不是和全排列有关,然后读了好几遍题目也没有想法。
  • 最后看了眼数据范围S串的长度为1000,\(O(N^2)\)是可以过的。
  • 然后我是这样想的,对于每一个位置都有可能成为答案的第一位,对于原串在它后面的字符在新串中不改变,在它前面的字符接在最后即可,然后对于每一个位置放在第一位构成字符串存起来,最后sort得到答案。
  • 看了眼别人的做法,比我的巧妙多了,直接用两个相同的字符串接上,对于每一个位求长度为\(s.size()\)的字串,更新答案即可。

代码如下

//当场做的
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
string ans[maxn];
int cnt;
int main()
{
        ios::sync_with_stdio(0);
        cin.tie(0);
        cout.tie(0);
        string s;
        cin >> s;
        string tmp;
        for (int i = 0; i < s.size(); i++)
        {
                tmp.clear();
                for (int j = i; j < s.size(); j++)
                {
                        tmp += s[j];
                }
                for (int j = 0; j < i; j++)
                {
                        tmp += s[j];
                }
                ans[++cnt] = tmp;
        }
        sort(ans + 1, ans + 1 + cnt);
        cout << ans[1] << endl;
        cout << ans[cnt] << endl;
        return 0;
}


//巧妙方法
#include <bits/stdc++.h>
using namespace std;
int main()
{
        ios::sync_with_stdio(0);
        cin.tie(0);
        cout.tie(0);
        string s;
        cin >> s;
        int len = s.size();
        string maxs = s;
        string mins = s;
        s = s + s;
        for (int i = 0; i < len; i++)
        {
                string tmp = s.substr(i, len);
                if (tmp > maxs)
                        maxs = tmp;
                if (tmp < mins)
                        mins = tmp;
        }
        cout << mins << endl;
        cout << maxs << endl;

        return 0;
}

C - Doukasen

思路分析:

  • 此题惯例模拟题
  • 模拟即可,我们知道如只有一端有火焰的话燃烧完需要多少时间,然后令时间减少一半求左端火焰燃烧多少长度即可。
  • 注意double相等的判断。

代码如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
double l[maxn];
double t[maxn];
double suml[maxn];
double sumt;
const double eps = 1e-6;
int main()
{
        ios::sync_with_stdio(0);
        cin.tie(0);
        cout.tie(0);
        int n;
        cin >> n;
        for (int i = 1; i <= n; i++)
        {
                double x;
                cin >> l[i];
                suml[i] = suml[i - 1] + l[i];
                cin >> x;
                t[i] = l[i] / x;
                sumt += t[i];
        }
        sumt /= 2;
        int i;
        double tmp = 0.0;
        for (i = 1; i <= n; i++)
        {
                tmp += t[i];
                if (tmp >= sumt)
                {
                        break;
                }
        }
        if (tmp - sumt > eps)
        {
                tmp -= t[i];
                i--;
                double ans = suml[i];
                ans += (sumt - tmp) * l[i + 1] / t[i + 1];
                cout << setprecision(15) << fixed << ans << endl;
        }
        else
                cout << setprecision(15) << fixed << suml[i] << endl;
        return 0;
}

D - Restricted Permutation

思路分析:

  • 题刷少了,经典的拓扑排序加优先队列做不出来。
  • 我们可以考虑第一个样例,所有边构成图后,有\(2,3\)入度为0,那么我们把它们取出来放入到优先队列里,然后来取,我们可以认为在当前状态实际上\(2,3\)的等级相同,题目要求最小的拓扑序列,那么我们就用优先级队列来替代普通队列来保证答案是最小字典序。

代码如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
int degree[maxn];
vector<int> e[maxn];
int ans[maxn];
int cnt;
int n, m;
priority_queue<int, vector<int>, greater<int>> q;
void add(int u, int v)
{
        degree[v] += 1;
        e[u].push_back(v);
}
void toposort()
{
        for (int i = 1; i <= n; i++)
        {
                if (degree[i] == 0)
                {
                        q.push(i);
                }
        }
        while (!q.empty())
        {
                int x = q.top();
                q.pop();
                ans[++cnt] = x;
                for (auto i : e[x])
                {
                        degree[i]--;
                        if (degree[i] == 0)
                        {
                                q.push(i);
                        }
                }
        }
}
int main()
{
        ios::sync_with_stdio(0);
        cin.tie(0);
        cout.tie(0);
        cin >> n >> m;
        for (int i = 1; i <= m; i++)
        {
                int u, v;
                cin >> u >> v;
                add(u, v);
        }
        toposort();
        if (cnt != n)
        {
                cout << -1 << endl;
                return 0;
        }
        for (int i = 1; i <= n; i++)
        {
                cout << ans[i] << ' ';
        }
        cout << endl;
        return 0;
}

E - Placing Rectangles

思路分析:

  • 我人傻了,一开始以为是边界分析问题,先安排面积最大的,其次是次大的,最后是最小的,但是wa且t了,想了很久没有想出来,就参考了官方题解补了这个题,我解释一遍官方题解的意思。
  • 第一个图说的是当我们有两个矩形时如果要满足题目的意思的话,我们必定可以找到一条平行于\(X\)轴或者\(Y\)轴的直线,把所给的区域分为两个部分,这两个部分每一个都有一个矩形。
  • 那么直线也就有两种情况,如果直线平行于\(X\)轴,我们直接把S这个矩形压到x轴上,那么求一下剩余的\(X\)的长度即为\(S\)的面积除以\(y\)轴长度(注意向上取整),反之亦然。
  • 三个矩形的话在两个矩形上来做就简单了,我们要把这三个矩形分为两个部分,一个部分放一个矩形,另外一个放两个矩形,看看是否满足条件即可。

官方题解的图的解释:

  • 1、两个矩形的时候我们去找线,可以找到好几条,但是如果要满足条件的话,剩下的一个矩形必然不能存在于两个红色部分当中(很显然的),实际上做的时候是把这个矩形压到轴上。

  • 2、三个矩形就在两个矩形的基础上就可以,这幅图就是说先把两个矩形安排了,然后安排剩下的一个矩形,实际上我们先安排一个是一样的,它这个图这样画是便于理解。

  • 3、这种情况就是上面部分两个或者是下面部分两个。

  • 4、这种一样的。

  • 那么有同学会想会不会讨论不完全,其实是很完全的。

  • 首先我们选了三种矩形各占一块的时候实际上就已经有了两种,即分别将这三个矩形压到\(x\)或者\(y\)轴,然后在判断剩下两个矩形又有两种,一共就是\(C_3^1* 2*2 = 12\)种情况,我们可以看到这样是肯定讨论完全的了。

代码如下:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
bool solve2(ll X, ll Y, ll S, ll T)
{
        for (int i = 0; i < 2; i++)
        {
                ll len = (S + X - 1) / X;
                //求剩下的长度
                if (len < Y && X * (Y - len) >= T)
                {
                        return 1;
                }
                //如果这个长度小于与这个轴垂直的轴的长度,并且剩下部分面积大于我们另一个矩形的面积的话
                swap(X, Y);
                //有两种情况
        }
        return 0;
}
bool solve3(ll X, ll Y, ll a, ll b, ll c)
{
        for (int i = 0; i < 2; i++)
        {
                for (int j = 0; j < 3; j++)
                {
                        ll len = (a + X - 1) / X;
                        //也就是说先安排了一个,然后搞剩下的两个
                        if (len < Y && solve2(X, Y - len, b, c))
                        {
                                return 1;
                        }
                        swap(a, b);
                        swap(b, c);
                        //这里是有三种情况的,我们安排其中一个,三个就是三种
                }
                swap(X, Y);
                //和solve2一样,大情况两种
        }
        return 0;
}
int main()
{
        ll X, Y, a, b, c;
        cin >> X >> Y >> a >> b >> c;
        cout << ((solve3(X, Y, a, b, c) == 1) ? "Yes" : "No") << endl;
        return 0;
}