AtCoder Beginner Contest 391题解

题目地址:https://atcoder.jp/contests/abc391

A题

映射或条件判断即可

#include <bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;

inline void init()
{
    string s;cin >> s;
    for(auto i : s)
    {
        if(i == 'N')cout << 'S';
        else if(i == 'S')cout << 'N';
        else if(i == 'E')cout << 'W';
        else if(i == 'W')cout << 'E';
    }
}

signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    init();
    return 0;
}

B题

要求在一个二维大数组中找出一个二维小数组,可先用两个for循环遍历大数组,找到与小数组的左上角元素相同的点。再从该点用两个for循环检查对应元素。

#include <bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;

const int N = 2510;
char s[N][N], p[N][N];

inline void init()
{
	//读入二维数组
    int n, m;cin >> n >> m;
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= n; j ++)
            cin >> s[i][j];
    for(int i = 1; i <= m; i ++)
        for(int j = 1; j <= m; j ++)
            cin >> p[i][j];

	//因为切入点是小数组左上角,所以a,b最大值为n - m + 1
    for(int a = 1; a <= n - m + 1; a ++)
    {
        for(int b = 1; b <= n - m + 1; b ++)
        {
            if(s[a][b] == p[1][1])
            {
                bool match = true;
                //如果match为false直接枚举下一个{a, b}
                for(int i = 1; i <= m && match; i ++)
                {
                    for(int j = 1; j <= m && match; j ++)
                    {
                        if(s[a + i - 1][b + j - 1] != p[i][j])
                            match = false;
                    }
                }
                if(match)
                {
                    cout << a << ' ' << b << endl;
                    return;
                }
            }
        }
    }
}

signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    init();
    return 0;
}

C题

题数据范围:2≤N≤1e6,1≤Q≤3e5。意味着每次操作至少得是O(logn)甚至O(1)。

首先对于操作2,需要快速输出满足条件的巢数的个数,则可以维护一个ans,在每次操作1时,根据巢内鸽子数的变化更新ans。
所以还需要维护一个数组,记录每个巢中鸽子数。
对于操作1,还需要知道每个鸽子此刻所在的巢,故维护一个数组pos。

#include <bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;

const int N = 1e6 + 9;
int cnt[N], pos[N], ans;

inline void init()
{
    int n, q;cin >> n >> q;
    for(int i = 1; i <= n; i ++)pos[i] = i, cnt[i] = 1;
    while(q --)
    {
        int op;cin >> op;
        if(op == 1)
        {
            int x, y;cin >> x >> y;
            // 需要维护cnt, ans, pos
            // old表示旧巢,cur表示新巢
            int old = pos[x], cur = y;
            // 维护cnt
            cnt[old] --, cnt[cur] ++;
            // 维护pos
            pos[x] = y;
            // 维护ans
            if(cnt[old] == 1)ans --;
            if(cnt[cur] == 2)ans ++;
        }
        else
            cout << ans << endl;
    }
}

signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    init();
    return 0;
}

D题

本题只有查询操作,不需要动态维护,考虑预处理出所有答案快速查询。

我们要研究某个时间时某个砖块是否存在。
根据样例显然可以发现一些砖块是不会被消去的,即同层不满的砖块。
对于可消去的砖块,因为消去只发生在最底一行,所以要等同层所有砖块到位,即消去需要的时间取决于同层最大的高度。
并且由于消去只能发生在最下面,对于第二层及其以上的砖块,需要等下面消完了才到自己,所以消去时间至少会大于等于上一层消去时间+1。

查询时有两个参数:时间t、编号x。我们只需预处理出每个编号x消去所需的时间,并与t比较即可。我们要做的是链接t和x。
根据上述分析,时间t取决于该层的最大高度以及前一层的t,对于前者,需要一个数据结构来存储每一列的所有高度,为了取出同一层,还需在一列中对高度排序,我们可以用vector<int> v[N],其中N为最大列数;后者只需要简单状态转移:times[i] = max(times[i - 1] + 1, times[i])。
现在我们能够根据消去层来得到时间,还需要链接消去层与编号x,也就是需要处理出一个数组rounds来映射消去层与x。消去层和位置信息是有密切关系的,通过vector可以用层来找到位置信息,我们可以再用位置信息与x链接,所以我们还需要一个map<pair<int, int>, int> pos,用于使用位置信息来找到编号x。

#include <bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;

const int N = 2e5 + 9;
vector<int> v[N]; //存储每列中的所有高度
int rounds[N], times[N]; //编号->层,层->时间
map<pair<int, int>, int> pos; //位置->编号

inline void init()
{
    int n, w;cin >> n >> w;
    for(int i = 1; i <= n; i ++)
    {
        int x, y;cin >> x >> y;
        v[x].push_back(y); //第x列,存储高度y
        pos[{x, y}] = i; //链接位置和编号
    }
    for(int i = 1; i <= w; i ++) //枚举每一列
    {
        sort(v[i].begin(), v[i].end()); //按高度排序
        for(int j = 1; j <= v[i].size(); j ++) //按高度从下向上枚举,j代表轮次
        {
            rounds[pos[{i, v[i][j - 1]}]] = j; //v[i][j - 1]为第j轮代表的高度
            times[j] = max(times[j], v[i][j - 1]); //times更新为每一层中的最大值
        }
        times[v[i].size() + 1] = 1e9; //边界情况,当某一层不满时,之后的times为很大的值
    }
    for(int i = 2; i < n; i ++)
        times[i] = max(times[i], times[i - 1] + 1); //确保times合法
    int q;cin >> q;
    while(q --)
    {
        int t, x;cin >> t >> x;
        int now = times[rounds[x]];
        cout << (now > t ? "Yes" : "No") << endl;
    }
}

signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    init();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值