vp Codeforces Round 986 (Div. 2)
A. Alice's Adventures in "Chess"
题意:你从(0, 0)出发,重复固定的移动路径,问能不能经过(a, b)。
直接跑一百次就行,因为ab都很小(其实只要跑20次)。
点击查看代码
void solve() {
int n, a, b;
std::cin >> n >> a >> b;
int x = 0, y = 0;
std::string s;
std::cin >> s;
std::vector<std::pair<int, int> > c;
for (auto & ch : s) {
if (ch == 'N') {
++ y;
} else if (ch == 'E') {
++ x;
} else if (ch == 'S') {
-- y;
} else {
-- x;
}
c.push_back({x, y});
}
for (int i = 0; i <= 100; ++ i) {
for (auto & it : c) {
if (it.first == a && it.second == b) {
std::cout << "YES\n";
return;
}
it.first += x; it.second += y;
}
}
std::cout << "NO\n";
}
B. Alice's Adventures in Permuting
题意:给你一个数组的生成式:\(a_i\)=(i-1)*b+c,每次把最大的数变成mex,问需要多少次可以变成0到n-1的排列。
自己写几个样例发现,如果全部数都小于n,那么b=1,c=0的情况是0次操作,b=0的话,c>=n是n次操作,c>=n-2是n-1次操作,其他都是-1。
对于数组里有大于等于n的数的情况,那么每次会把一个大于等于n的数变成mex,如果有m个大于等于n的数,就会操作m次,我是用二分找的这个m。
点击查看代码
void solve() {
using i128 = __int128;
i64 n, b, c;
std::cin >> n >> b >> c;
if (b == 0 && c >= n) {
std::cout << n << "\n";
return;
}
if (b == 0 && c >= n - 2) {
std::cout << n - 1 << "\n";
return;
}
if ((i128)(n - 1) * (i128)b + c == n - 1) {
std::cout << 0 << "\n";
return;
}
if ((i128)(n - 1) * (i128)b + c < n - 1) {
std::cout << -1 << "\n";
return;
}
i64 l = 1, r = n;
while (l < r) {
i64 mid = l + r >> 1;
if ((i128)(mid - 1) * (i128)b + c >= n) {
r = mid;
} else {
l = mid + 1;
}
}
std::cout << n - l + 1 << "\n";
}
C. Alice's Adventures in Cutting Cake
题意:你要把一个数组分成m+1组,每一组都由连续的元素组成,其中m组每组总和要大于等于v,要让剩下属于我们的一组在满足这些条件下的总和最大。
如果我们以i开始,那么前面i-1个应该正好分成满足条件的几组,不然我们的开始位置可以往前移。那么我们可以从前往后枚举前面分几组,那么我们这组能取到后面哪个位置呢?为了让后面的组对我们的影响最小,应该从最后开始分给他们,那就预处理从后面开始到第i个可以取满几个组,那么如果我们前面有了k组,那么可以二分后面分成m-k组的最大下标,那么中间的数就都属于我们。
点击查看代码
void solve() {
int n, m;
i64 v;
std::cin >> n >> m >> v;
std::vector<i64> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::vector<i64> sum(n + 1);
for (int i = 0; i < n; ++ i) {
sum[i + 1] = sum[i] + a[i];
}
std::vector<int> cnt(n + 2);
for (int i = n; i >= 1; -- i) {
int j = i;
while (j && sum[i] - sum[j - 1] < v) {
-- j;
}
if (j) {
cnt[j] = cnt[i + 1] + 1;
}
i = j;
}
for (int i = n; i >= 1; -- i) {
cnt[i] = std::max(cnt[i], cnt[i + 1]);
}
if (cnt[1] < m) {
std::cout << -1 << "\n";
return;
};
i64 ans = 0;
for (int i = 1, k = m; i <= n; ++ i, -- k) {
int l = i + 1, r = n + 1;
while (l < r) {
int mid = l + r + 1 >> 1;
if (cnt[mid] >= k) {
l = mid;
} else {
r = mid - 1;
}
}
if (cnt[l] >= k) {
ans = std::max(ans, sum[l - 1] - sum[i - 1]);
}
int j = i;
while (j <= n && sum[j] - sum[i - 1] < v) {
++ j;
}
i = j;
}
std::cout << ans << "\n";
}
D. Alice's Adventures in Cards
题意:我们手里有一个1,希望它变到n,我们可以和三个人交换,每个人对i都有一个重视值,如果第i个数重视值大于第j个数,那么我们可以用j换i。我们只可以换比手里更大的数。要求给定一个操作序列。
正解好像是dp,我写了个差不多搜索的东西。
三个set存分别存{\(a_i\), i}, {\(b_i\), i}, {\(c_i\), i}。
开始我们把1入队,用优先队列每次出最小的数。然后把还没进队的比当前数小的数从三个set里都删了。然后三个set分别lower_bound一下枚举比当前数值小的数,求个最短路记录转移,最后把新入队的数统一从三个set里删了。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n + 1), b(n + 1), c(n + 1);
for (int i = 1; i <= n; ++ i) {
std::cin >> a[i];
}
for (int i = 1; i <= n; ++ i) {
std::cin >> b[i];
}
for (int i = 1; i <= n; ++ i) {
std::cin >> c[i];
}
std::vector<int> pre(n + 1);
std::vector<std::string> op(n + 1);
std::priority_queue<int, std::vector<int>, std::greater<int> > q;
q.push(1);
std::set<int> s;
std::set<std::pair<int, int> > sa, sb, sc;
for (int i = 2; i <= n; ++ i) {
s.insert(i);
sa.insert({a[i], i});
sb.insert({b[i], i});
sc.insert({c[i], i});
}
while (q.size()) {
int u = q.top(); q.pop();
while (s.size() && *s.begin() < u) {
int id = *s.begin();
sa.erase({a[id], id});
sb.erase({b[id], id});
sc.erase({c[id], id});
s.erase(s.begin());
}
std::vector<int> del;
auto ia = sa.lower_bound({a[u], 0});
while (ia != sa.begin()) {
-- ia;
int v = ia->second;
if (op[v].empty()) {
pre[v] = u;
op[v] = "Q";
q.push(v);
del.push_back(v);
}
}
auto ib = sb.lower_bound({b[u], 0});
while (ib != sb.begin()) {
-- ib;
int v = ib->second;
if (op[v].empty()) {
pre[v] = u;
op[v] = "K";
q.push(v);
del.push_back(v);
}
}
auto ic = sc.lower_bound({c[u], 0});
while (ic != sc.begin()) {
-- ic;
int v = ic->second;
if (op[v].empty()) {
pre[v] = u;
op[v] = "J";
q.push(v);
del.push_back(v);
}
}
for (auto & id : del) {
sa.erase({a[id], id});
sb.erase({b[id], id});
sc.erase({c[id], id});
s.erase(id);
}
}
if (s.count(n)) {
std::cout << "NO\n";
} else {
std::cout << "YES\n";
std::vector<std::string> ans;
int i = n;
while (i > 1) {
ans.push_back(op[i] + " " + std::to_string(i));
i = pre[i];
}
std::reverse(ans.begin(), ans.end());
std::cout << ans.size() << "\n";
for (auto & s : ans) {
std::cout << s << "\n";
}
}
}
E. Alice's Adventures in the Rabbit Hole
题意:有一棵树,1号点是出口,叶节点是死路,每次有二分之一的概率由你决定移动到哪个相邻节点,否则由皇后决定,皇后希望你走到叶子上,你希望走到出口上。求每个点逃脱的概率。
我们肯定是往上爬,皇后会选择离叶子近的那一边。我们的决策显然是这样,而皇后因为不想让我们到出口,肯定不会帮我们往上爬,而她又要让我们去叶子,肯定是越近的越好。
设f[u]表示这个节点逃脱成功的概率。
根节点概率是1,叶子是0,其他概率是f[u]=\(\frac{1}{2}\)(f[fa]+fa[v]),离叶子节点距离1的话是f[u]=\(\frac{1}{2}\)f[fa],离叶子节点距离2就是f[u]=\(\frac{1}{2}\)(f[fa]+fa[v])=\(\frac{1}{2}\)(f[fa]+\(\frac{1}{2}\)f[u])->\(\frac{3}{4}\)f[u]=\(\frac{1}{2}\)f[fa],整理得f[u]=\(\frac{2}{3}\)f[fa]。我们可以继续算,也可以猜一下,发现f[u]=f[fa]\(\frac{d[u]}{(d[u]+1)}\)。d[u]是u到叶子的最短距离。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<std::vector<int> > adj(n);
for (int i = 1; i < n; ++ i) {
int u, v;
std::cin >> u >> v;
-- u, -- v;
adj[u].push_back(v);
adj[v].push_back(u);
}
std::vector<int> d(n, 1e9);
auto dfs = [&](auto self, int u, int fa) -> void {
for (auto & v : adj[u]) {
if (v == fa) {
continue;
}
self(self, v, u);
d[u] = std::min(d[u], d[v] + 1);
}
if (d[u] == 1e9) {
d[u] = 0;
}
};
dfs(dfs, 0, -1);
std::vector<Z> f(n);
auto dfs1 = [&](auto self, int u, int fa) -> void {
for (auto & v : adj[u]) {
if (v == fa) {
continue;
}
f[v] = f[u] * (Z)d[v] / (Z)(d[v] + 1);
self(self, v, u);
}
};
f[0] = 1;
dfs1(dfs1, 0, -1);
for (int i = 0; i < n; ++ i) {
std::cout << f[i] << " \n"[i == n - 1];
}
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析