C
在回转寿司店里面有n个标为
A
i
A_i
Ai人和m个标为
B
j
B_j
Bj寿司。
每个寿司依次从1 2 3 … n人面前经过,当
B
j
≥
A
i
B_j \geq A_i
Bj≥Ai时i人就拿走寿司。
问:每个寿司被谁吃了?
对寿司进行排序,依次从1 2 3 … n人来看每个人怎么拿。
排序之后会发现,每个人一定会把最前面的那些满足条件的寿司拿走。然后下一个人接着拿。因此可以用一个指针指向当前拿到的寿司位置。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
int n, m;
const int N = 200020;
struct Item {
int x, i;
} b[N];
int a[N];
int ans[N];
int main(){
//freopen("in.txt", "r", stdin);
cin >> n >> m;
for (int i = 1; i <= n; ++ i) {
cin >> a[i];
}
for (int i = 1; i <= m; ++ i) {
int t;
cin >> t;
b[i] = {t, i};
}
sort(b + 1, b + m + 1, [&](Item x, Item y){
return x.x > y.x;
});
memset(ans, -1, sizeof(ans));
int j = 0;
for(int i = 1; i <= n; ++ i) {
while(j + 1 <= m && b[j + 1].x >= a[i]) {
ans[b[j + 1].i] = i;
++ j;
}
}
for (int i = 1; i <= m; ++ i) {
printf("%d\n", ans[i]);
}
return 0;
}
D
搜索数列。写的时候注意剪枝。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
vector<vi> ans;
int n, m;
int path[12];
void go(int pos, int cur) {
if (cur > m)
return;
path[pos] = cur;
if (pos == n - 1) {
vi temp(path, path + n);
ans.push_back(temp);
return;
}
int lmt = cur + (n - pos - 1) * 10;
if (lmt > m)
return;
for (int i = 10; i <= 20; ++i) {
go(pos + 1, cur + i);
}
}
int main(){
//freopen("in.txt", "r", stdin);
cin >> n >> m;
for (int a0 = 1; a0 <= min(m, 20); ++a0) {
go(0, a0);
}
printf("%d\n", ans.size());
for (auto vi : ans) {
for (int x : vi) {
printf("%d ", x);
}
printf("\n");
}
return 0;
}
E
有无数个包,每个包里面都有N张卡片,每张卡片是稀有卡的概率是
p
i
p_i
pi
一次只能打开一个包,然后拿出所有卡片抽卡。
问:抽到S张稀有卡片或以上,需要打开包的数量的数学期望。
经典数学期望模型。
这个题有两个子问题。第一个子问题是每次打开一个包,在单独一个包内抽到
t
t
t张稀有卡的分布。
第二个子问题是已知离散分布
t
t
t,求
∑
i
=
1
k
t
i
≥
S
\sum^k_{i=1} t_i \geq S
∑i=1kti≥S时次数k的期望。
第一问可以用经典dp来做:设
g
(
i
,
j
)
g(i,j)
g(i,j)为抽到第i张卡片时有j张稀有卡的概率
g
(
i
,
j
)
=
g
(
i
−
1
,
j
)
∗
q
i
+
g
(
i
−
1
,
j
)
∗
p
i
g(i,j)=g(i-1,j)*q_i+g(i-1,j)*p_i
g(i,j)=g(i−1,j)∗qi+g(i−1,j)∗pi 其中
q
i
q_i
qi是非稀有的概率
=
(
1
−
p
i
)
=(1-p_i)
=(1−pi)
最后的分布概率
p
(
t
)
=
g
(
n
,
t
)
p(t)=g(n,t)
p(t)=g(n,t)
第二问是数学期望求法中常见的技巧:
设
E
(
X
)
E(X)
E(X)代表总和能拿到
X
X
X或以上的数量稀有卡片需要的期望次数。这里不是exactly X次,而用了
s
≥
X
s \geq X
s≥X
E
(
x
)
=
(
E
(
0
)
p
(
x
)
+
E
(
1
)
p
(
x
−
1
)
+
.
.
+
E
(
x
)
p
(
0
)
)
+
1
E(x)=(E(0)p(x)+E(1)p(x-1)+..+E(x)p(0))+1
E(x)=(E(0)p(x)+E(1)p(x−1)+..+E(x)p(0))+1
注意:这里将每一项期望都拆开成和式就会发现公式成立
移项可得
(
1
−
p
(
0
)
)
E
(
x
)
=
1
+
∑
i
=
0
x
−
1
E
(
i
)
p
(
x
−
i
)
(1-p(0))E(x)=1+\sum_{i=0}^{x-1} E(i)p(x-i)
(1−p(0))E(x)=1+∑i=0x−1E(i)p(x−i)
依次递推即可求得
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
double f[5005][5005];
double g[5005];
double p[5005];
int n, X;
int main(){
//freopen("in.txt", "r", stdin);
cin >> n >> X;
for (int i = 1; i <= n; ++i) {
int t;
cin >> t;
p[i] = double(t) / 100;
}
f[0][0] = 1;
for (int i = 1; i <= n; ++i) {
f[i][0] = f[i - 1][0] * (1 - p[i]);
for (int j = 1; j <= X; ++j) {
f[i][j] = f[i - 1][j - 1] * p[i] + f[i - 1][j] * (1 - p[i]);
}
}
double r = 1 - f[n][0];
g[0] = 0;
for (int i = 1; i <= X; ++i) {
double s = 0;
for (int j = 1; j < i; ++j) {
s += g[i - j] * f[n][j];
}
g[i] = (1 + s) / r;
}
printf("%.12f\n", g[X]);
return 0;
}
F
线段树
从下往上排序,落下来的线段要检查[l,r]上最大值,然后落在最大值+1的行上。
然后这条线段对[l,r]区间更新最大值+1
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
int h, w, n;
const int N = 200020;
struct Item {
int r, c, l, i;
} a[N];
int ans[N];
#define lu (u * 2)
#define ru (u * 2 + 1)
struct SegTree {
struct Node {
int l, r, mx, mark;
} tr[N << 2];
void up(int u) {
tr[u].mx = max(tr[lu].mx, tr[ru].mx);
}
void down(int u) {
if (tr[u].mark) {
tr[lu].mark = tr[ru].mark = tr[u].mark;
tr[lu].mx = tr[ru].mx = tr[u].mx;
tr[u].mark = 0;
}
}
void build(int l, int r, int u) {
tr[u].l = l, tr[u].r = r;
if(l == r) {
tr[u].mx = 0;
return;
}
int mi = (l + r) / 2;
build(l, mi, lu);
build(mi + 1, r, ru);
}
void update(int l, int r, int u, int val) {
if (l <= tr[u].l && tr[u].r <= r) {
tr[u].mark = 1;
tr[u].mx = val;
return;
}
down(u);
int mi = (tr[u].l + tr[u].r) / 2;
if (l <= mi) {
update(l, r, lu, val);
}
if (r > mi) {
update(l, r, ru, val);
}
up(u);
}
int query(int l, int r, int u) {
if (l <= tr[u].l && tr[u].r <= r) {
return tr[u].mx;
}
down(u);
int rt = 0;
int mi = (tr[u].l + tr[u].r) / 2;
if (l <= mi) {
rt = max(rt, query(l, r, lu));
} if (r > mi) {
rt = max(rt, query(l, r, ru));
}
return rt;
}
} st;
int main(){
//freopen("in.txt", "r", stdin);
cin >> h >> w >> n;
for (int i = 1; i <= n; ++i) {
int r, c, l;
cin >> r >> c >> l;
a[i] = { r, c, l, i };
}
sort(a + 1, a + n + 1, [](Item x, Item y) {
return x.r > y.r;
}
);
st.build(1, w, 1);
for (int i = 1; i <= n; ++i) {
int l = a[i].c, r = a[i].c + a[i].l - 1;
int max_h = st.query(l, r, 1);
int cur_h = max_h + 1;
ans[a[i].i] = cur_h;
st.update(l, r, 1, cur_h);
}
for (int i = 1; i <= n; ++i) {
printf("%d\n", h + 1 - ans[i]);
}
return 0;
}
G
感谢snuke选手的漂亮代码
这个题的精髓在于预搜索,如果硬做是很难写的,那么可以预先两边都bfs几步,将格子里面的不同情况统一成一种情况,然后再算需要几步完成
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
int n;
ll sx, sy, tx, ty;
ll k;
struct Grid {
ll x, y, i;
void set(ll xpos, ll ypos) {
x = xpos / k, y = ypos / k;
if (parity()) {
i = xpos % k;
}
else {
i = ypos % k;
}
}
int parity() {
return (x + y) % 2;
}
bool operator == (const Grid& rhs) const {
return x == rhs.x && y == rhs.y && i == rhs.i;
}
vector<Grid> neighbors() {
vector<Grid> ret;
if (parity()) { // |
ret.push_back({ x, y - 1, k - 1 });
ret.push_back({ x, y + 1, 0 });
if (i == 0) {
ret.push_back({ x - 1, y, 0 });
ret.push_back({ x - 1, y, k - 1 });
}
else if (i == k - 1) {
ret.push_back({ x + 1, y, 0 });
ret.push_back({ x + 1, y, k - 1 });
}
if (i > 0)
ret.push_back({ x, y, i - 1 });
if (i < k - 1)
ret.push_back({ x, y, i + 1 });
}
else { // --
ret.push_back({ x - 1, y, k - 1 });
ret.push_back({ x + 1, y, 0 });
if (i == 0) {
ret.push_back({ x, y - 1, 0 });
ret.push_back({ x, y - 1, k - 1 });
}
else if (i == k - 1) {
ret.push_back({ x, y + 1, 0 });
ret.push_back({ x, y + 1, k - 1 });
}
if (i > 0)
ret.push_back({ x, y, i - 1 });
if (i < k - 1)
ret.push_back({ x, y, i + 1 });
}
return ret;
}
tuple<ll, ll, ll> get_tuple() {
return { x, y, i };
}
};
auto enum_near(Grid& g) {
vector<pair<ll, Grid>> res;
res.push_back({ 0, g });
set<tuple<ll, ll, ll>> s;
s.insert(g.get_tuple());
for (int i = 0; i < 2000; ++i) {
auto [d, p] = res[i];
if (d >= 3)
break;
for (auto& np : p.neighbors()) {
if (s.count(np.get_tuple()))
continue;
res.push_back({ d + 1, np });
}
}
return res;
}
ll get_dist(Grid s, Grid t) {
if (s == t)
return 0;
const ll MX = 1e18;
if (s.i != 0 && s.i != k - 1)
return MX;
if (t.i != 0 && t.i != k - 1)
return MX;
if (s.parity() != t.parity() || s.i != t.i)
return MX;
ll dx = abs(s.x - t.x), dy = abs(s.y - t.y);
if (dx > dy) swap(dx, dy);
ll res = dx * 2;
ll d = dy - dx;
res += d / 2 * (k == 2 ? 3 : 4);
return res;
}
void work() {
cin >> k;
cin >> sx >> sy >> tx >> ty;
ll offset = ll(1e17) / (k * 2) * (k * 2);
sx += offset, sy += offset, tx += offset, ty += offset;
Grid s, t;
s.set(sx, sy);
t.set(tx, ty);
auto near_s = enum_near(s), near_t = enum_near(t);
ll ans = 1e18;
for (const auto &[d0, s] : near_s) {
for (const auto &[d1, t] : near_t) {
ll r = get_dist(s, t);
ll temp = d0 + d1 + r;
ans = min(ans, temp);
}
}
printf("%lld\n", ans);
}
int main(){
//freopen("in.txt", "r", stdin);
int t;
cin >> t;
while (t--) {
work();
}
return 0;
}