A.Arithmetic Progression(模拟)
题意:
输出首项为 a a a,末项为 b b b,公差为 d d d的等差数列。
分析:
按照要求模拟。
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int a, b, d;
cin >> a >> b >> d;
for (int i = a; i <= b; i += d) {
cout << i << " ";
}
return 0;
}
B.Langton’s Takahashi(模拟)
题意:
给定一个空的序列, q q q次询问。有以下两种操作:
- 序列末尾加入一个新元素 x x x
- 询问从末尾开始往前数第 k k k个元素是多少。
分析:
用 v e c t o r vector vector模拟即可。
代码:
#include <bits/stdc++.h>
using namespace std;
vector<int> a;
int main() {
int q;
cin >> q;
for (int i = 1; i <= q; i++) {
int op, x;
cin >> op >> x;
if (op == 1) {
a.push_back(x);
} else {
cout << a[a.size() - x] << endl;
}
}
return 0;
}
C.Divide and Divide(dfs)
题意:
黑板上有一个整数 n n n,执行以下操作,直到所有不小于 2 2 2的整数都从黑板上移除。
- 选择写在黑板上的一个不小于 2 2 2的整数 x x x
- 移除 x x x,并在黑板上写下 ⌊ x / 2 ⌋ \lfloor x /2 \rfloor ⌊x/2⌋ , ⌈ x / 2 ⌉ \lceil x/2 \rceil ⌈x/2⌉
- 支付 x x x元来完成操作。
询问所有操作结束后,需要支付多少元。可以证明,无论操作的顺序如何,支付的总金额是不变的。
分析:
按照要求分为向下取整和向上取整两种搜索方向进行记忆化搜索即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
map<LL, LL> dp;
LL dfs(LL x) {
if (x == 2) {
return dp[x] = 2;
}
if (x == 1) {
return dp[x] = 0;
}
if (dp.count(x)) {
return dp[x];
}
return dp[x] = (x + 1) / 2 + x / 2 + dfs((x + 1) / 2) + dfs(x / 2);
}
int main() {
LL n;
cin >> n;
cout << dfs(n) << endl;
return 0;
}
D Super Takahashi Bros.(最短路)
题意:
有一个闯关游戏由
n
n
n个关卡组成,最初玩家停留在关卡
1
1
1。
对于每一个关卡
i
(
1
≤
i
≤
n
−
1
)
i (1 \le i \le n-1)
i(1≤i≤n−1)可以执行以下两个操作中的一个:
- 花费 a i a_i ai的时间,攻克关卡 i i i,进入 i + 1 i+1 i+1。
- 花费 b i b_i bi的时间,攻克关卡 i i i,进入 x i x_i xi。
询问至少需要多少时间可以通过通关。
分析:
最短路建边。每个点 i i i向 i + 1 i+1 i+1连一条代价为 a i a_i ai的边,向 x i x_i xi连一条代价为 b i b_i bi的边,跑最短路即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 2e5 + 5;
LL a[MAXN], b[MAXN], x[MAXN];
LL vis[MAXN], dis[MAXN];
priority_queue<pair<LL, LL>> q;
const LL INF = 1e18;
int n;
void dijkstra() {
dis[n] = INF;
dis[1] = 0;
q.push(make_pair(-dis[1], 1));
while (q.size() > 0) {
int u = q.top().second;
q.pop();
if (vis[u] == 1)
continue;
vis[u] = 1;
if (u < n) {
if (dis[u + 1] > dis[u] + a[u]) {
dis[u + 1] = dis[u] + a[u];
q.push(make_pair(-dis[u + 1], u + 1));
}
if (dis[x[u]] > dis[u] + b[u]) {
dis[x[u]] = dis[u] + b[u];
q.push(make_pair(-dis[x[u]], x[u]));
}
}
}
}
int main() {
cin >> n;
for (int i = 1; i < n; i++)
cin >> a[i] >> b[i] >> x[i], dis[i] = INF;
dijkstra();
cout << dis[n] << endl;
return 0;
}
E Mancala 2(树状数组)
题意:
给出 n n n个盒子,每个盒子里有 a i a_i ai个球,盒子编号从 0 − n − 1 0-n-1 0−n−1。接着对 i = 1 , 2 , 3... m i=1,2,3...m i=1,2,3...m进行如下操作:
- 变量 c = 0 c=0 c=0
- 从编号为 b i b_i bi的盒子中拿出所有的球
-
在至少有一个球的时候执行如下操作:
1. c + = 1 1.c+=1 1.c+=1。
2. 2. 2.将一个球放入 ( b i + c ) % n (b_i+c)\%n (bi+c)%n的盒子里。
询问所有操作结束后,每个盒子里球的个数。
分析:
第三个操作类似区间加法,使用树状数组进行简单的区间加法即可。查询每个球的数量就是树状数组的区间查询。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 2e5 + 5;
#define lowbit(x) (x & (-x))
LL tr[MAXN], n, m;
LL a[MAXN], b[MAXN];
LL query(LL x) {
LL ans = 0;
while (x > 0)
ans += tr[x], x -= lowbit(x);
return ans;
}
void add(LL l, LL r, LL v) {
while (l <= n)
tr[l] += v, l += lowbit(l);
r++;
while (r <= n)
tr[r] -= v, r += lowbit(r);
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> a[i], add(i, i, a[i]);
for (int i = 1; i <= m; i++)
cin >> b[i], b[i]++;
for (int i = 1; i <= m; i++) {
LL x = b[i];
LL sum = query(x);
add(x, x, -sum);
LL p = sum / n;
add(1, n, p);
sum %= n;
if (sum + x <= n)
add(x + 1, x + sum, 1);
else
add(x + 1, n, 1), add(1, sum + x - n, 1);
}
for (int i = 1; i <= n; i++)
cout << query(i) << " ";
return 0;
}
F S = 1(数学)
题意:
给出整数 x , y x,y x,y,至少满足 x , y x,y x,y中的其中一个不等于 0 0 0。请找出满足以下条件的一对整数 a , b a,b a,b。
- − 1 0 18 ≤ a , b ≤ 1 0 18 -10^{18} \le a,b \le 10^{18} −1018≤a,b≤1018
- ( 0 , 0 ) , ( x , y ) , ( a , b ) (0,0),(x,y),(a,b) (0,0),(x,y),(a,b)三点构成的三角形面积为 1 1 1。
分析:
利用叉积进行分析,将题目转化成 x × b − y × a = ∣ 2 ∣ x \times b - y \times a= \vert 2 \vert x×b−y×a=∣2∣,使用扩展欧几里得求得一组可行解即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL n, m;
LL exgcd(LL a, LL b, LL &x, LL &y) {
if (b == 0LL) {
x = 1LL;
y = 0LL;
return a;
}
LL x1, y1, gcd;
gcd = exgcd(b, a % b, x1, y1);
x = y1, y = x1 - a / b * y1;
return gcd;
}
int main() {
cin >> n >> m;
LL x, y;
LL tmp = exgcd(m, -n, x, y);
tmp = abs(tmp);
if (2 % tmp != 0)
cout << "-1" << endl;
else if (tmp) {
if (x == 0 and n == 0)
swap(x, y);
if (y == 0 and m == 0)
swap(x, y);
cout << 2LL / tmp * x << " " << 2LL / tmp * y << endl;
}
return 0;
}
G Leaf Color(树上启发式合并)
题意:
给一棵 n n n个点无根树,每个点的颜色为 a i a_i ai。询问这棵树有多少个子图 T T T,满足 T T T是一棵树,且 T T T每个叶节点的颜色都相同。
分析:
设 d p [ u ] [ i ] dp[u][i] dp[u][i]表示以 u u u为根,且叶子节点颜色为 i i i的子树数量,当 c o l u = i col_u=i colu=i时需要加上根的度数为 1 1 1的情况。当新加入一个儿子时, d p [ u ] [ i ] = ( ( d p [ u ] [ i ] × d p [ v ] [ i ] ) % m o d + d p [ u ] [ i ] + d p [ v ] [ i ] ) % m o d ; dp[u][i] = ((dp[u][i] \times dp[v][i]) \% mod + dp[u][i] + dp[v][i]) \% mod; dp[u][i]=((dp[u][i]×dp[v][i])%mod+dp[u][i]+dp[v][i])%mod;。同时此时的贡献一定是根的度数大于等于 2 2 2的子树贡献,所以贡献需要继续增加 d p [ u ] [ i ] × d p [ v ] [ i ] dp[u][i] \times dp[v][i] dp[u][i]×dp[v][i]。利用启发式合并降低复杂度完成转移。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 998244353;
const int MAXN = 2e5 + 5;
map<LL, LL> dp[MAXN];
set<int> col[MAXN];
int son[MAXN], size1[MAXN];
vector<int> edge[MAXN];
int a[MAXN];
int n, ans;
void dfs1(int u, int fa) {
size1[u] = 1;
for (int v: edge[u]) {
if (v == fa)
continue;
dfs1(v, u);
if (size1[v] > size1[son[u]])
son[u] = v;
size1[u] += size1[v];
}
}
void dfs2(int u, int fa) {
for (auto v: edge[u]) {
if (v != fa)
dfs2(v, u), ans += dp[v][a[u]], ans %= mod;
}
if (son[u] != 0)
swap(dp[u], dp[son[u]]), swap(col[u], col[son[u]]);
for (auto v: edge[u]) {
if (v != fa && v != son[u]) {
for (int x: col[v]) {
ans += (dp[u][x] * dp[v][x] % mod);
ans %= mod;
dp[u][x] = ((dp[u][x] * dp[v][x]) % mod + dp[u][x] + dp[v][x]) % mod;
col[u].insert(x);
}
}
}
dp[u][a[u]] = (dp[u][a[u]] + 1) % mod;
ans++;
ans %= mod;
col[u].insert(a[u]);
return;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 2; i <= n; i++) {
int u, v;
cin >> u >> v;
edge[u].push_back(v);
edge[v].push_back(u);
}
dfs1(1, 0);
dfs2(1, 0);
cout << ans << endl;
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。