第十六届蓝桥杯软件赛省赛C++ B组 题解。
A. 移动距离
首先不难发现一个简单的达到目的地的方案。
然后只需证明这个最简单的方案也是最优的就可以了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include<bits/stdc++.h> #define int long long using namespace std;
void solve(){ double x = 233, y = 666; double l = sqrt(x * x + y * y);
cout << fixed << setprecision(0); cout << l + l * atan2(y, x) << '\n'; }
signed main(){ ios::sync_with_stdio(0); cin.tie(0); solve(); return 0; }
|
B. 客流量上限
$$
A_i\ne 2025 (i\le 2024)\leftarrow A_i^2\le i^2+2025
$$
于是 $A_{2025}=2025$.
$$
A_i\ne 2024 (i\le 2023)\leftarrow A_i^2\le i^2+2025
$$
于是 $A_{2024}=2024$.
….
重复此过程,右侧能推得左侧的条件是:
$$
(t+1)^2>t^2+2025\rightarrow t> 1012
$$
所以 $A_i=i\ (i\ge 1013)$。
接下来,令 $A_j=2025$,推得 $A_i\times2025\le A_i\times2025+2025$,即 $A_i\le i+1\ (i\le 1012)$。
最后证明 $A_i=i\ (i\ge 1013)$,$A_i\le i+1\ (i\le 1012)$ 也是满足题设的充分条件即可。
分为三种情况讨论:
- $i,j\le 1012$,$A_iA_j\le(i+1)(j+1)=ij+i+j+1\le ij+2025$,满足。
- $i\le1012,j\ge1013$,$A_iA_j\le(i+1)j\le ij+j\le ij+2025$,满足。
- $i,j\ge 1013$,$A_iA_j\le ij\le ij+2025$,满足。
所以:
$A_1$ 可取 1,2
$A_2$ 可取 1,2,3
$A_3$ 可取 1,2,3,4
…
$A_{1012}$ 可取 1,2,3,4,…,1013
每个数都有两种选择(在前面的数选完的基础上)。
所以答案为 $2^{1012} (mod\ 1e9+7)$。
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
| #include<bits/stdc++.h> #define int long long using namespace std;
constexpr int mod = 1e9 + 7;
int qmi(int a, int k, int p){ int res = 1; while(k){ if(k & 1) res = res * a % p; a = a * a % p; k >>= 1; } return res; }
void solve(){ cout << qmi(2, 1012, mod) << '\n'; }
signed main(){ ios::sync_with_stdio(0); cin.tie(0); solve(); return 0; }
|
C. 可分解的正整数
类似一个构造题,可以发现,任何大于 1 的数 $n$ 都可以被表示为 :
$$
-(n-1),-(n-2),…,n-2,n-1,n
$$
且序列长度至少有 3,只有 1 无法被三个连续递增的整数表示。
输出非 1 的个数即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include<bits/stdc++.h> #define int long long using namespace std;
void solve(){ int n; cin >> n;
int res = 0; while(n--){ int x; cin >> x; res += x != 1; }
cout << res << '\n'; }
signed main(){ ios::sync_with_stdio(0); cin.tie(0); solve(); return 0; }
|
D. 产值调整
第一眼看似乎没有很好的优化方法,但细看发现,三个数的差值基本在以一个 $\log$ 的速度收敛,所以直接模拟该过程,知道三个数相等为止。
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
| #include<bits/stdc++.h> #define int long long using namespace std;
void solve(){ int a, b, c, k; cin >> a >> b >> c >> k;
for(int i = 0; i < k; i++){ int u = b + c >> 1; int v = a + c >> 1; int w = a + b >> 1; a = u, b = v, c = w; if(a == b && b == c) break; }
cout << a << ' ' << b << ' ' << c << '\n'; }
signed main(){ ios::sync_with_stdio(0); cin.tie(0); int t; cin >> t; while(t--) solve(); return 0; }
|
E. 画展布置
显然排序后取连续的一段可以让答案最优,找出最优的一段即可。
(排序后,求和的结果可以将绝对值打开,最后就是 $B_M^2-B_1^2$ 即可)
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
| #include<bits/stdc++.h> #define int long long using namespace std;
void solve(){ int n, m; cin >> n >> m; vector<int> a(n); for(auto &i : a) cin >> i;
sort(a.begin(), a.end());
int res = 1e18; for(int i = 0; i + m - 1 < n; i++){ res = min(res, a[i + m - 1] * a[i + m - 1] - a[i] * a[i]); }
cout << res << '\n'; }
signed main(){ ios::sync_with_stdio(0); cin.tie(0); solve(); return 0; }
|
F. 水质检测
先想出最优的方案,然后模拟你想出来的方案即可。
写了一个小状压,将每一列压成一个状态,从第一次找到 #
开始,准备填充。
进入准备填充状态后,每次遇到新的 #
,找一个最优的填充方案,然后将 last 状态更新即可。
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
| #include<bits/stdc++.h> #define int long long using namespace std;
void solve(){ string a, b; cin >> a >> b; int n = a.size();
vector<int> dp(n); for(int i = 0; i < n; i++){ dp[i] = (a[i] == '#') + (b[i] == '#') * 2; }
int fd = 0, last = 0, pos = 0, res = 0; for(int i = 0; i < n; i++){ if(!dp[i]) continue;
if(!fd){ fd = 1; last = dp[i]; } else{ if(last & dp[i]){ res += i - pos - 1; last = dp[i]; } else{ res += i - pos; last = 3; } }
pos = i; }
cout << res << '\n'; }
signed main(){ ios::sync_with_stdio(0); cin.tie(0); solve(); return 0; }
|
G. 生产车间
实质是,每次用子节点可达的数去更新父节点可达的数。
每次更新是一个卷积的过程,子节点可达 $a_i$,父节点可达 $b_i$,那么更新后所有可达的值为 $a_i+b_j$ 的所有组合。(所以有大佬用 fft 实现可以达到 $O(n\log n)$)
当然,我们用一个 bitset 优化基本时间就够了。更新过程用移位加速,更新完保留低 w[u]
位的值。
最后看根节点的最大可达值即可。
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
| #include<bits/stdc++.h> #define int long long using namespace std;
void solve(){ int n; cin >> n; vector<int> w(n); for(auto &i : w) cin >> i;
vector<vector<int>> adj(n); for(int i = 1; i < n; i++){ int u, v; cin >> u >> v; u--, v--; adj[u].emplace_back(v); adj[v].emplace_back(u); }
vector<bitset<1001>> dp(n); function<void(int, int)> dfs = [&](int u, int fa){ dp[u][0] = 1; if(u && adj[u].size() == 1){ dp[u][w[u]] = 1; return; }
bitset<1001> tmp; tmp[0] = 1; for(auto &v : adj[u]){ if(v == fa) continue; dfs(v, u); for(int i = 0; i < 1001; i++){ if(dp[u][i]){ tmp |= dp[v] << i; } } swap(dp[u], tmp); } dp[u] <<= (1000 - w[u]); dp[u] >>= (1000 - w[u]); }; dfs(0, -1);
int res = 0; for(int i = w[0]; i >= 0; i--){ if(dp[0][i]){ res = i; break; } }
cout << res << '\n'; }
signed main(){ ios::sync_with_stdio(0); cin.tie(0); solve(); return 0; }
|
H. 装修报价
观察这么一个随意的式子:
$$
a+b\oplus c-d+e
$$
我们很容易找出其对偶(我们暂且这么称呼):
$$
a-b\oplus c+d-e
$$
此两者的和为 $2a$,不妨记每一个的贡献为 $a$ 好了。
不难发现所有的式子都可以如此配对,有的贡献为 $a$,有的贡献为 $a\oplus b$,…
动手模拟一下就可以看出,这些贡献都是这个序列的异或前缀,我们只需计算出每个异或前缀出现的次数就好了。
对于贡献为 $a$ 的,紧跟其后的符号有 +
-
两个选择,其余符号任意,因此出现$2\times 3^{n-2}$ 次,对于贡献为 $a\oplus b$ 的,类似分析下去 …
需要注意全体序列异或的出现次数需要特判一下,为 1。
整理一下,答案应该为:
$$
\sum_{i=0}^{n-2} \oplus_{k=0}^{i} a_i\times 2\times 3^{n-2-i} + \oplus_{k=0}^{n-1} a_i
$$
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
| #include<bits/stdc++.h> #define int long long using namespace std;
constexpr int mod = 1e9 + 7;
void solve(){ int n; cin >> n; vector<int> a(n); for(int i = 0; i < n; i++) cin >> a[i];
vector<int> p3(n, 1); for(int i = 1; i < n; i++) p3[i] = p3[i - 1] * 3 % mod;
int pre = 0;
int res = 0; for(int i = 0; i < n - 1; i++){ pre ^= a[i]; res += pre * 2 * p3[n - 2 - i]; res %= mod; } pre ^= a[n - 1]; res = (res + pre) % mod;
cout << res << '\n'; }
signed main(){ ios::sync_with_stdio(0); cin.tie(0); solve(); return 0; }
|