Atcoder Beginner Contest 395记录
AtCoder Beginner Contest 395记录
A - Strictly Increasing?
模拟
思路
直接遍历一遍就行
代码
神奇的代码
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
const int maxn = 2e5 + 5;
const int inf = 0x7f7f7f7f;
int nums[110];
void solve()
{
int n = 0;
std::cin >> n;
for (int i = 1; i <= n; i++) std::cin >> nums[i];
for (int i = 1; i < n; i++)
{
if (nums[i] >= nums[i + 1])
{
std::cout << "No" << endl;
return;
}
}
std::cout << "Yes" << endl;
}
signed main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr); std::cout.tie(nullptr);
//freopen("out.txt", "w", stdout);
int t = 1;
//std::cin >> t;
while(t--)
{
solve();
}
return 0;
}
B - Make Target
模拟
思路
找到规律直接模拟
要涂色的图形是左上和右下坐标分别为\((i, i)\)和\((n + 1 - i, n + 1 - i)\)的正方形边框,而且\(i\)是奇数,一层一层染色就好
代码
神奇的代码
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
char mat[55][55];
void solve()
{
memset(mat, '.', sizeof(mat));
int n = 0;
std::cin >> n;
for (int i = 1; i <= (n + 1) / 2; i += 2)
{
for (int j = i; j <= n + 1 - i; j++)
{
mat[i][j] = '#';
mat[j][i] = '#';
mat[n + 1 - i][j] = '#';
mat[j][n + 1 - i] = '#';
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
std::cout << mat[i][j];
}
std::cout << endl;
}
}
signed main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr); std::cout.tie(nullptr);
//freopen("out.txt", "w", stdout);
int t = 1;
//std::cin >> t;
while(t--)
{
solve();
}
return 0;
}
C - Shortest Duplicate Subarray
思维
思路
可以记录每一个数上次出现的位置,这个数再次出现时就更新答案
也可以使用滑动窗口,这里用的滑动窗口,有些麻烦了
代码
神奇的代码
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
int nums[200010];
std::deque<int> q;
int cnt[1000010];
void solve()
{
int n = 0;
std::cin >> n;
for (int i = 1; i <= n; i++)
{
std::cin >> nums[i];
}
int ans = 1e12;
for (int i = 1; i <= n; i++)
{
if(q.empty())
{
cnt[nums[i]]++;
q.push_back(nums[i]);
continue;
}
q.push_back(nums[i]);
cnt[nums[i]]++;
if (cnt[nums[i]] == 2)
{
while(cnt[nums[i]] == 2)
{
int t = q.front();
q.pop_front();
cnt[t]--;
}
ans = std::min(ans, (int)q.size() + 1);
}
}
if (ans == 1e12)
{
std::cout << -1 << endl;
}
else
{
std::cout << ans << endl;
}
}
signed main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr); std::cout.tie(nullptr);
//freopen("out.txt", "w", stdout);
int t = 1;
//std::cin >> t;
while(t--)
{
solve();
}
return 0;
}
D - Pigeon Swap
思维
思路
不要看错问题,操作2是把\(a\)和\(b\)鸽巢交换位置,不是把其中一个的鸽子移动到另外一个
鸽巢的位置是变化的,但是交换鸽巢的时候鸽子的位置不变化,可以维护鸽子在哪个位置,鸽巢在哪个位置,每个位置上放的是哪一个鸽巢
代码
神奇的代码
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
const int maxn = 1e6 + 5;
const int inf = 0x7f7f7f7f;
int faz[maxn]; // faz[i]鸽子i在哪一个位置
int pos[maxn]; // pos[i]笼子i在哪一个位置
int mp[maxn]; // mp[i]位置i是哪一个笼子
void solve()
{
int n = 0, q = 0;
std::cin >> n >> q;
for (int i = 1; i <= n; i++)
{
faz[i] = i;
pos[i] = i;
mp[i] = i;
}
int k = 0, a = 0, b = 0;
for (int i = 1; i <= q; i++)
{
std::cin >> k;
if(k == 1)
{
std::cin >> a >> b;
faz[a] = pos[b];
}
else if (k == 2)
{
std::cin >> a >> b;
int t1 = pos[a], t2 = pos[b];
pos[a] = t2;
pos[b] = t1;
mp[t1] = b;
mp[t2] = a;
}
else
{
std::cin >> a;
std::cout << mp[faz[a]] << endl;
}
}
}
signed main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr); std::cout.tie(nullptr);
//freopen("out.txt", "w", stdout);
int t = 1;
//std::cin >> t;
while(t--)
{
solve();
}
return 0;
}
E - Flip Edge
最短路,动态规划,思维
思路
有点类似于分层图?
多了一种可以反转边的操作,那么在使用\(Dijkstra\)的时候就多了一种状态
\(dp[i][0/1]\):到达顶点\(i\)时此时全局是反转前/后的状态的最小花费
因为有反转状态,所以存图的时候反边也要存下来,但是要记录状态,这里为了方便可以用链式前向星存图
struct Edge
{
int u, v, st, nxt;
}edge[maxn << 1];
st为0时说明这条边是反转之前的边,st为1时说明这条边是反转之后的边
状态转移:
当走边\((u, v)\)时,若\(st = 0\), 对\(dp[v][0]\)操作, \(dp[v][0]\)可由\(dp[u][1]\)或\(dp[u][0]\)转移,前者需要反转而后者不需要,\(st = 1\)是类似
if (edge[i].st == 0)
{
if(dp[tt][0] > dp[t][0] + 1)
{
dp[tt][0] = dp[t][0] + 1;
q.push({tt, dp[tt][0], 0});
}
if (dp[tt][0] > dp[t][1] + x + 1)
{
dp[tt][0] = dp[t][1] + x + 1;
q.push({tt, dp[tt][0], 0});
}
}
else
{
if (dp[tt][1] > dp[t][1] + 1)
{
dp[tt][1] = dp[t][1] + 1;
q.push({tt, dp[tt][1], 1});
}
if (dp[tt][1] > dp[t][0] + x + 1)
{
dp[tt][1] = dp[t][0] + x + 1;
q.push({tt, dp[tt][1], 1});
}
}
\(update\):额,就是分层图,写法复杂了,有时间在补一下
代码
神奇的代码
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
const int maxn = 2e5 + 5;
const int inf = 0x7f7f7f7f;
int cnt = 0;
int head[maxn];
int vis[maxn][2];
int dp[maxn][2]; // dp[i][1/0] 到达顶点i当前状态是/否反转的最小花费
struct Edge
{
int u, v, st, nxt;
}edge[maxn << 1];
struct Node
{
int to, w, st;
bool operator < (const Node &t) const
{
return w > t.w;
}
};
std::priority_queue<Node> q;
void AddEdge(int u, int v, int st)
{
edge[++cnt].u = u;
edge[cnt].v = v;
edge[cnt].st = st;
edge[cnt].nxt = head[u];
head[u] = cnt;
}
int n = 0, m = 0, x = 0;
void dij()
{
q.push({1, 0, 0});
dp[1][0] = 0;
while(!q.empty())
{
Node tmp = q.top();
q.pop();
if (vis[tmp.to][tmp.st]) continue;
vis[tmp.to][tmp.st] = 1;
int t = tmp.to;
for (int i = head[t]; i != -1; i = edge[i].nxt)
{
int tt = edge[i].v;
if (edge[i].st == 0)
{
if(dp[tt][0] > dp[t][0] + 1)
{
dp[tt][0] = dp[t][0] + 1;
q.push({tt, dp[tt][0], 0});
}
if (dp[tt][0] > dp[t][1] + x + 1)
{
dp[tt][0] = dp[t][1] + x + 1;
q.push({tt, dp[tt][0], 0});
}
}
else
{
if (dp[tt][1] > dp[t][1] + 1)
{
dp[tt][1] = dp[t][1] + 1;
q.push({tt, dp[tt][1], 1});
}
if (dp[tt][1] > dp[t][0] + x + 1)
{
dp[tt][1] = dp[t][0] + x + 1;
q.push({tt, dp[tt][1], 1});
}
}
}
}
}
void solve()
{
std::cin >> n >> m >> x;
for (int i = 1; i <= n; i++)
{
head[i] = -1;
dp[i][0] = dp[i][1] = 1e18;
}
int u, v;
for (int i = 1; i <= m; i++)
{
std::cin >> u >> v;
AddEdge(u, v, 0);
AddEdge(v, u, 1);
}
dij();
std::cout << std::min(dp[n][0], dp[n][1]);
}
signed main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr); std::cout.tie(nullptr);
//freopen("out.txt", "w", stdout);
int t = 1;
//std::cin >> t;
while(t--)
{
solve();
}
return 0;
}
F - Smooth Occlusion
思路
花费最多的情况就是\(H = 0\),把所有的变成\(0\),假设总花费为\(s\)
如果找到一个合法的\(H\),总花费就成了\(s - H * n\), \(H\)在合法范围内越大越好\(\rightarrow\)二分答案
那么如何\(check\)呢
当\(H\)一定时,花费确定,不用想怎么使得花费最小,我们可以找出一个\(u[i]\)的取值区间判断这个区间合不合法
对于第一个牙齿区间就是\([max(0, h - d[1]), min(u[i], h)]\)
对于第\(i(2 \leq i)\)个牙齿首先要在区间\([tleft = max(0, h - d[i]),\ \ tright = min(u[i]), h]\)中,其次最小值还可以要拓展到上一个(区间的最小值)- x, 最大值要拓展到上一个(区间最大值) + x
然后判断区间合不合法就好了:区间左端点<=区间右端点
代码
神奇的代码
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
const int maxn = 2e5 + 5;
const int inf = 0x7f7f7f7f;
struct custom_hash
{
static uint64_t splitmix64(uint64_t x)
{
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
return x;
}
size_t operator () (uint64_t x) const
{
static const uint64_t FIXED_RANDOM = std::chrono::steady_clock::now().time_since_epoch().count(); // 时间戳
return splitmix64(x + FIXED_RANDOM);
}
};
int u[maxn], d[maxn];
int n = 0, x = 0;
bool check(int h)
{
int mn = std::max(h - d[1], 0ll);
int mx = std::min(u[1], h);
if (mx < mn) return false;
for (int i = 2; i <= n; i++)
{
int tle = std::max(h - d[i], 0ll);
int tri = std::min(u[i], h);
int tmn = std::max(tle, mn - x);
int tmx = std::min(tri, mx + x);
if (tmx < tmn) return false;
mn = tmn, mx = tmx;
}
return true;
}
void solve()
{
std::cin >> n >> x;
int s = 1e18, sum = 0;
for (int i = 1; i <= n; i++)
{
std::cin >> u[i] >> d[i];
s = std::min(s, u[i] + d[i]);
sum += u[i] + d[i];
}
int l = -1, r = s + 1;
while(l + 1 < r)
{
int h = (l + r) >> 1;
if(check(h))
{
l = h;
}
else
{
r = h;
}
}
std::cout << sum - l * n << endl;
}
signed main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr); std::cout.tie(nullptr);
//freopen("out.txt", "w", stdout);
int t = 1;
//std::cin >> t;
while(t--)
{
solve();
}
return 0;
}

浙公网安备 33010602011771号