2024-06-30 23:42阅读: 873评论: 4推荐: 5

AtCoder Beginner Contest 360

A - A Healthy Breakfast (abc360 A)

题目大意

给定一个字符串包含RMS,问R是否在S的左边。

解题思路

比较RS的下标,谁小即谁在左边。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
string s;
cin >> s;
cout << (s.find('R') < s.find('M') ? "Yes" : "No") << '\n';
return 0;
}


B - Vertical Reading (abc360 B)

题目大意

给定两个字符串s,t,找到一个 1cw<|s|,使得将 s拆开,每个子串有w个字母, 对每个子串取第c位字母出来(不存在则不取),使其组成 t

解题思路

由于|s|100,直接花 O(|s|2)枚举 c,w,然后拆开子串、拼接、比较即可。时间复杂度是 O(n3)

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
string s, t;
cin >> s >> t;
bool ok = false;
for (int i = 1; i < s.size(); i++) {
vector<string> sub;
for (int j = 0; j < s.size(); j += i) {
sub.push_back(s.substr(j, i));
}
for (int j = 0; j < i; ++j) {
string ans;
for (auto& x : sub) {
if (j < x.size())
ans += x[j];
}
if (ans == t) {
ok = true;
}
}
}
if (ok)
cout << "Yes" << '\n';
else
cout << "No" << '\n';
return 0;
}


C - Move It (abc360 C)

题目大意

给定 n个箱子。再给定n个球所在的箱子位置,球有重量。

移动球到其他箱子里,代价是球的重量。

求最小的代价,使得每个箱子各有一个球。

解题思路

对于一个有多个球的盒子,因为最终会有一个球留在盒子里,为了最小代价,那最重的球不移动,而移动其他的球。

因此最终的代价就是每个盒子除最重球的球重量的和。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
vector<vector<int>> box(n);
vector<int> pos(n);
for (auto& i : pos)
cin >> i;
for (int i = 0; i < n; ++i) {
int w;
cin >> w;
box[pos[i] - 1].push_back(w);
}
int ans = 0;
for (auto& i : box) {
if (i.empty())
continue;
sort(i.begin(), i.end(), greater<int>());
ans += accumulate(i.begin(), i.end(), 0) - i[0];
}
cout << ans << '\n';
return 0;
}


D - Ghost Ants (abc360 D)

题目大意

一维数轴,蚂蚁移动,有方向,速度一样。碰撞时不改变方向,继续保持原方向行走。

经过t时刻后,问碰撞的蚂蚁对数。

解题思路

同方向的蚂蚁不会碰撞,因此仅考虑不同向的。

考虑往右的蚂蚁会与哪些往左的蚂蚁碰撞。

由于所有蚂蚁速度一样,因此由相向运动得知,假设当前往右的蚂蚁位置为x,那么所有往左的位于[x,x+2t]的蚂蚁都会与其相撞。

根据蚂蚁前进方向对坐标排序,二分一下就能找到往左的位于 [x,x+2t]的蚂蚁数量。

所有数量累加即为答案。时间复杂度是 O(nlogn)

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, t;
string dir;
cin >> n >> t >> dir;
array<vector<int>, 2> pos;
for (int i = 0; i < n; ++i) {
int p;
cin >> p;
pos[dir[i] - '0'].push_back(p);
}
for (auto& x : pos)
sort(x.begin(), x.end());
LL ans = 0;
for (int i = 1; i < 2; ++i) {
auto& cur = pos[i];
auto& nxt = pos[i ^ 1];
for (auto x : cur) {
LL l = x;
LL r = x + t * (i == 1 ? 1 : -1) * 2ll;
if (l > r)
swap(l, r);
auto ll = lower_bound(nxt.begin(), nxt.end(), l);
auto rr = upper_bound(nxt.begin(), nxt.end(), r);
ans += rr - ll;
}
}
cout << ans << '\n';
return 0;
}


E - Random Swaps of Balls (abc360 E)

题目大意

n个球,其中第一个是黑球,其余是白球。

进行以下操作 k次:

  • 随机两次独立选取 i,j,交换第 i个球和第 j个球。

问黑球位置的期望值。

解题思路

假设最终黑球位于第i个球的概率是pi,根据期望定义,答案就是 i=1nipi。考虑如何求pi

容易发现第 2,3,...,n个球没有本质区别,其概率都是一样的,因此最终我们需要求的就两个数

  • p1即位于第一个球的概率
  • p2即位于非第一个球的概率

容易发现经过第k次操作的概率,仅依赖于第 k1次的情况,因此可以迭代球,即设 dp[k][0/1]表示经过 k次操作后,球位于第一个球/不位于第一个球的概率。

转移就考虑本次操作是否将球移动到第一个球或非第一个球。设移动概率move=21nn1n,不移动概率stay=1move,移动到某一个位置的概率为 move1=moven1=2n2

dp[i][0]=dp[i1][0]×stay+dp[i1][1]×move1,不动或者从非一球移动到一球。
dp[i][1]=dp[i1][0]×move+dp[i1][1]×(1move1),从一球移动或者从非一球移动到非移动(全概率-移动到一球的概率)

最后p1=dp[k][0],p2=p3=...=pn=dp[k][1]n1,答案就是i=1nipi=dp[k][0]+(n+2)(n1)2dp[k][1]n1

时间复杂度为O(k)

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int mo = 998244353;
long long qpower(long long a, long long b) {
long long qwq = 1;
while (b) {
if (b & 1)
qwq = qwq * a % mo;
a = a * a % mo;
b >>= 1;
}
return qwq;
}
long long inv(long long x) { return qpower(x, mo - 2); }
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, k;
cin >> n >> k;
int invn = inv(n);
int invn1 = inv(n - 1);
int move = 2ll * invn * (n - 1) % mo * invn % mo;
int stay = (1ll - move + mo) % mo;
int move1 = 1ll * move * invn1 % mo;
array<int, 2> dp{};
dp[0] = 1;
for (int i = 0; i < k; i++) {
array<int, 2> dp2{};
dp2[0] = (1ll * dp[0] * stay % mo + 1ll * dp[1] * move1 % mo) % mo;
dp2[1] = (1ll * dp[0] * move % mo +
1ll * dp[1] * ((1 - move1 + mo) % mo) % mo) %
mo;
dp.swap(dp2);
}
int ans =
(dp[0] + 1ll * (n + 2) * (n - 1) / 2 % mo * dp[1] % mo * invn1 % mo) %
mo;
cout << ans << '\n';
return 0;
}


F - InterSections (abc360 F)

题目大意

给定n个区间,若俩区间[la,ra],[lb,rb]有交叉 ,则有la<lb<ra<rb

f(l,r)表示与 [l,r]有交叉的区间数。

求最大值。

解题思路

<++>

神奇的代码


G - Suitable Edit for LIS (abc360 G)

题目大意

给定一个数组,进行一次操作,使得最长上升子序列的长度最大。

操作为,将任意一个数改为任意一个数。

解题思路

这种有一次修改的,可以从修改处考虑,其修改处左右两边是一个正常的最长上升子序列问题。

因此事先求出pre[i]表示从左到右,选了第 i个数字的最长上升子序列长度,suf[i]表示从右到左,选了第 i个数字的最长下降子序列长度,需要使用O(nlogn)的方法。考虑如何将 presuf组合。

注意到是严格上升,如果枚举 sufi,思考 max(prej+sufi+1)有什么条件,以及 j如何找。

枚举 i,考虑 j,则有 k(j,i),ak需要修改,因此 j<i1,同时由于严格递增, ai>aj+1 ,这样ak才能更改成对应的数,使得 prejsufi拼接起来得到一个更长的最长上升子序列。

即需要解决这么一个问题maxj<i1&ai>aj+1(prej+sufi+1)。容易发现这就是一个朴素的二维偏序问题,用线段树求解即可。注意需要对ai离散化。

O(nlogn)的一种最长上升子序列的求法,即pos[i]表示最长上升子序列长度为 i的末尾数字(最大数字)的最小值。注意到 pos是一个递增的数组,因此对于当前数字ai ,可以二分找到它可以接在哪个数字aj的后面,进而知道了当前的dp[i],然后更新 pos数组。或者朴素的 O(n2)dp,分析转移式可以注意到也是一个二维偏序问题,也可以用线段树解决。

用线段树解二维偏序,下标是aj,值是dp[j]。即当前dp[j]求出来后,将 dp[j1]插入到线段树里,即将 aj位置的值赋予 dp[j]

这样在求dp[i]时,线段树里的值都是 j<i1dp[j]的值,自然满足第一个 j<i1的条件,因为线段树的下标是 aj,而 ai>aj+1相当于一个区间 [1,ai2],要求的就是这个区间的dp最大值,因为线段树维护的就是dp[j],故区间查询最大值、单点修改即可。而由于ai很大,为作为线段树的下标,需要离散化。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int inf = 1e9 + 8;
const int N = 2e5 + 8;
class segment {
#define lson (root << 1)
#define rson (root << 1 | 1)
public:
int maxx[N << 2];
void build(int root, int l, int r) {
if (l == r) {
maxx[root] = 0;
return;
}
int mid = (l + r) >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
maxx[root] = max(maxx[lson], maxx[rson]);
}
void update(int root, int l, int r, int pos, int val) {
if (l == r) {
maxx[root] = max(maxx[root], val);
return;
}
int mid = (l + r) >> 1;
if (pos <= mid)
update(lson, l, mid, pos, val);
else
update(rson, mid + 1, r, pos, val);
maxx[root] = max(maxx[lson], maxx[rson]);
}
LL query(int root, int l, int r, int L, int R) {
if (L <= l && r <= R) {
return maxx[root];
}
int mid = (l + r) >> 1;
int resl = -inf, resr = -inf;
if (L <= mid)
resl = query(lson, l, mid, L, R);
if (R > mid)
resr = query(rson, mid + 1, r, L, R);
return max(resl, resr);
}
} sg;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
vector<int> a(n);
for (auto& x : a)
cin >> x;
auto solve = [&](vector<int>& a) -> vector<int> {
vector<int> dp(n, 1);
vector<int> pos(n + 1, inf);
pos[0] = -inf;
for (int i = 0; i < n; i++) {
auto it = lower_bound(pos.begin(), pos.end(), a[i]) - pos.begin();
dp[i] = it;
pos[it] = min(pos[it], a[i]);
}
return dp;
};
auto pre = solve(a);
reverse(a.begin(), a.end());
transform(a.begin(), a.end(), a.begin(), [](int x) { return -x; });
auto suf = solve(a);
reverse(suf.begin(), suf.end());
reverse(a.begin(), a.end());
transform(a.begin(), a.end(), a.begin(), [](int x) { return -x; });
vector<int> b(a.begin(), a.end());
b.push_back(-inf);
sort(b.begin(), b.end());
b.erase(unique(b.begin(), b.end()), b.end());
auto rank = [&](int x) {
return lower_bound(b.begin(), b.end(), x) - b.begin() + 1;
};
int ans = 0;
int m = b.size();
sg.build(1, 1, m);
for (int i = 0; i < n; ++i) {
int cnt = sg.query(1, 1, m, 1, rank(a[i] - 1) - 1);
ans = max(ans, suf[i] + cnt + (i != 0));
if (i > 0)
sg.update(1, 1, m, rank(a[i - 1]), pre[i - 1]);
}
ans = max(ans, sg.query(1, 1, m, 1, m) + 1);
cout << ans << '\n';
return 0;
}


本文作者:~Lanly~

本文链接:https://www.cnblogs.com/Lanly/p/18277192

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   ~Lanly~  阅读(873)  评论(4编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.