题目地址: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;
}