ABC255比赛笔记

比赛传送门

前言

做出来 7 题,第一次做出 Ex,感觉还不错。

上一次做出 7 题还是在很久以前呢 /wx

为啥 ABC 前几题越来越难了啊,感觉 C 都有 D 的水平了。

交错程序一发,可真有我的。

题解

E - Lucky Numbers

题意

有一个未知的长度为 $N$ 的数列 $A$ 满足 $A_i+A_{i+1}=S_i$,给定 $S$ 和一个大小为 $M$ 的集合 $X$,求最多有多少个 $i$ 满足 $A_i \in X$。

$N \le 10^6,M \le 10,-10^9 \le S_i,X_i \le 10^9$

做法

Tag: 数学

先钦定 $A_1=0$,推出整个 $A$。

容易得到,如果 $A_1+1$,那么必定会有 $A_2-1,A_3+1,A_4-1,A_5+1\dots$,即奇数下标的 $+1$,偶数下标的 $-1$。

对于每个奇数的 $i$ 处理出所有 $X_j-A_i$,对于每个偶数的 $i$ 处理出所有 $A_i-X_j$,这样就可以统计出 $A_1+W$ 后的答案,即为 $W$ 在统计中出现的次数。在左右出现次数中取最大值即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
ll n, m, s[100005], x[20], a[100005], ans = 0;
vector<ll>v;
void solve() {
cin >> n >> m;
rep(i, n - 1)cin >> s[i];
rep(i, m)cin >> x[i];
forr(i, 1, n - 1)a[i] = s[i - 1] - a[i - 1];
rep(i, n) {
rep(j, m) {
ll tmp;
if (i % 2 == 0)tmp = a[i] - x[j];
else tmp = x[j] - a[i];
v.pb(tmp);
}
}
sort(v.begin(), v.end());
ll l, r = 0;
while (r < (ll)v.size()) {
l = r;
while (r + 1 < (ll)v.size() && v[r + 1] == v[l])r++;
ans = max(ans, r - l + 1);
r++;
}
cout << ans;
}

F - Pre-order and In-order

题意

给出一棵树的前序遍历 $P$ 和中序遍历 $I$,求这棵树的形态,或判断无解。

$N \le 2\times 10^5$

做法

CSP 初赛经典题了属于是。

考虑 $P_{l\dots r}$ 匹配 $I_{L\dots R}$。

显然 $P_l$ 为根,所以就可将 $I$ 划分成两段,即 $P_l$ 的左边和右边。知道左右子树大小后,就可以将 $P_{L+1\dots R}$ 划分成两段,递归即可。

如果 $P_l$ 在 $I_{L\dots R}$ 中没有出现则判断无解。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int n, a[200005], b[200005], p[200005];
int son[200005][2];
bool ans = 1;
void dfs(int l, int r, int L, int R) {
if (l > r || L > R || !ans)return;
int x = a[l], y = p[a[l]], lenl = y - L, lenr = R - y;
if (y<L || y>R)ans = 0;
if (lenl > 0)son[x][0] = a[l + 1] + 1, dfs(l + 1, l + lenl, L, y - 1);
if (lenr > 0)son[x][1] = a[l + lenl + 1] + 1, dfs(l + lenl + 1, r, y + 1, R);
}
void solve() {
cin >> n;
rep(i, n)cin >> a[i], a[i]--;
rep(i, n)cin >> b[i], b[i]--, p[b[i]] = i;
if (a[0]) {
cout << -1;
return;
}
dfs(0, n - 1, 0, n - 1);
if (!ans)cout << -1;
else rep(i, n)cout << son[i][0] << ' ' << son[i][1] << endl;
}

G - Constrained Nim

没看。

Ex - Range Harvest Query

题意

有 $N$ 棵树,第 $0$ 天它们都没有果子,每天晚上,第 $i$ 棵树会结出 $i$ 个果子。

共有 $Q$ 次采摘,第 $i$ 次在第 $D_i$ 天晚上,采走第 $L_i$ 到 $R_i$ 棵树上所有的果子。

对于每次采摘,输出采到了多少果子,对 $998244353$ 取模。

$N,D_i \le 10^{18},Q \le 2 \times 10^5,D_i<D_{i+1}$

做法

Tag: 数据结构,珂朵莉树

珂朵莉树模板题。

感谢 lxl!

维护每棵树最后被采摘的区间,每次用等差数列求和处理区间贡献,再区间赋值即可。

由于每次操作只会 split 出 至多 $2$ 个区间,所以区间总数是 $O(Q)$ 的,并且查询复杂度和包含的区间个数有关,查询后会将这些区间全部合并到一起,所以最多只会查询 $O(Q)$ 个区间,算上 set 复杂度,总时间复杂度为 $O(Q \log Q)$。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
struct Node {
ll l, r;
mutable ll val;
Node(ll a = -1, ll b = -1, ll c = 0) {
l = a, r = b, val = c;
}
bool operator < (const Node& a) const {
return l < a.l;
}
};
set<Node>s;
bool cmp(pair<ll, ll>x, pair<ll, ll>y) {
return x.first < y.first;
}
set<Node>::iterator split(ll pos) {
set<Node>::iterator it = s.lower_bound(Node(pos));
if (it != s.end() && it->l == pos)return it;
--it;
Node tmp = *it;
s.erase(it);
s.insert(Node(tmp.l, pos - 1, tmp.val));
return s.insert(Node(pos, tmp.r, tmp.val)).first;
}
void assign(ll l, ll r, ll val) {
set<Node>::iterator itr = split(r + 1), itl = split(l);
s.erase(itl, itr);
s.insert(Node(l, r, val));
}
ll qpow(ll a, ll x, ll y) {
ll ans = 1;
a %= y;
while (x) {
if (x & 1)ans = (ans * a) % y;
a = (a * a) % y;
x >>= 1;
}
return ans;
}
ll e_2;
ll query(ll l, ll r, ll d) {
set<Node>::iterator itr = split(r + 1), itl = split(l);
ll ans = 0;
for (set<Node>::iterator it = itl; it != itr; it++) {
ll L = it->l, R = it->r, lst = it->val;
ll sum = (L % MOD + R % MOD) % MOD * ((R - L + 1) % MOD) % MOD * e_2 % MOD;
ans = (ans + (d % MOD - lst) % MOD * sum % MOD) % MOD;
}
return (ans + MOD) % MOD;
}
ll n, q, d, l, r;
void solve() {
e_2 = qpow(2, MOD - 2, MOD);
cin >> n >> q;
s.insert(Node(1, n, 0));
while (q--) {
cin >> d >> l >> r;
cout << query(l, r, d) << endl;
assign(l, r, d);
}
}

后记

赛后难度出来了,果然 G>Ex 呢 /hx

作者

Rushroom

发布于

2023-01-06

更新于

2023-03-24

许可协议

CC BY-NC-SA 4.0

评论