2020蓝桥杯选拔赛题解
A
因为两条线段是平行于 x x x轴的, 只要考虑这两条线段再 x x x轴的投影是否相交。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
ll x[5], y[3];
cin >> x[1] >> x[2] >> x[3] >> x[4] >> y[1] >> y[2];
// l和r是那个直线的相交的线段
ll l = max(x[1], x[3]);
ll r = min(x[2], x[4]);
if (l <= r) {
puts("Yes");
} else {
puts("No");
}
}
B
考虑一种贪心的思想, 每次选费用最少的牌,一定可以取最多, 那么可以将费用从小到大排序,每次都取最小的。 每打出一张牌其它费用都减一, 这个可以用个计数器记录一下当前已经减了多少。时间复杂度为 o ( n ∗ l o n g ( n ) ) o(n * long(n)) o(n∗long(n))
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 7;
int n, a[N];
ll x;
int main() {
cin >> n >> x;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
sort(a + 1, a + n + 1);
int sum = 0; // 记录已经减了多少。
int ans = 0;
for (int i = 1; i <= n; i++) {
a[i] = max(a[i] - sum, 0); // 减到0就不会在减了
if (x >= a[i]) {
x -= a[i];
ans++;
} else {
break;
}
sum++;
}
cout << ans << endl;
}
C
这题简单模拟。直接看代码吧。
#include<bits/stdc++.h>
using namespace std;
string str;
typedef long long ll;
int main() {
cin >> str;
ll x = 0, y = 0;
ll w = 1, s = 1, a = 1, d = 1;
for (int i = 0; i <str.length(); i++) {
if (str[i] == 'W') {
x += w;
w++;
s = 1;
a = 1;
d = 1;
} else if (str[i] == 'S') {
x -= s;
s++;
w = 1;
a = 1;
d = 1;
} else if (str[i] == 'A') {
y -= a;
a++;
w = 1;
d = 1;
s = 1;
} else {
y += d;
d++;
a = 1;
w = 1;
s = 1;
}
}
cout << x << " " << y << endl;
}
D
查找一个字符串的子序列的数量是一个很常见的 d p dp dp问题。
但是这题可以用组合数学来解决。
枚举每个字符, 当遇到字符 S S S时,只要记录从开始到当前位置有多少个 C C C,再记录从当前位置到最后面的位置
有多少个 U U U。假设从开始到当前位置有 x x x个字符 C C C, 从当前位置到最后面有 y y y个 U U U,那么这个字符 S S S对答案的贡献就是
C x 2 ∗ y C_x ^{2} * y Cx2∗y
说一下 d p dp dp怎么写, 设 d p [ 0 ] dp[0] dp[0]表示当前位置有多少个字符 C C C , d p [ 1 ] dp[1] dp[1]表示有多少个字符 C C CC CC (这里的 C C CC CC指的是子序列), d p [ 2 ] dp[2] dp[2]表示有多少个 C C S CCS CCS(这里的 C C S CCS CCS也是子序列), d p [ 3 ] dp[3] dp[3]表示有多少个 C C S U CCSU CCSU(同理也是子序列)。
然后方程怎么转移?
d p [ 0 ] + = 1 dp[0] += 1 dp[0]+=1 当且仅当当前字符串为 C C C
d p [ 1 ] + = d p [ 0 ] dp[1] += dp[0] dp[1]+=dp[0] 当且仅当当前字符串为 C C C
d p [ 2 ] + = d p [ 1 ] dp[2] += dp[1] dp[2]+=dp[1]当且仅当当前字符串为 S S S
d p [ 3 ] + = d p [ 2 ] dp[3] += dp[2] dp[3]+=dp[2]当且仅当当前字符串为 U U U
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
string s;
ll dp[4];
const ll mod = 1e9 + 7;
int main() {
cin >> s;
for (int i = 0; i < s.length(); i++) {
if (s[i] == 'C') {
dp[1] += dp[0];
dp[0] += 1;
} else if (s[i] == 'S') {
dp[2] += dp[1];
} else if (s[i] == 'U') {
dp[3] += dp[2];
}
dp[0] = dp[0] % mod;
dp[1] = dp[1] % mod;
dp[2] = dp[2] % mod;
dp[3] = dp[3] % mod;
}
cout << dp[3] << endl;
}
E
每个物品可以考虑选和不选,那么总共也就 2 n 2 ^ n 2n种购买方案, 但是 n ≤ 32 n\le 32 n≤32 也就是最坏的情况时间复杂度为 o ( 2 32 ) o(2 ^{32}) o(232)大概是$ 4 * 10^9$左右时间复杂度肯定过不了。
所以考虑折半搜索。
将 n n n个物品分为两部分, [ 1 , n 2 ] [1, \frac {n}{2}] [1,2n] 的物品归为一类, 和 [ n 2 + 1 , n ] [\frac {n}{2} + 1, n] [2n+1,n]的物品归为一类。
那么现在就可以枚举第一类的所有可能尽快, 也就 2 n 2 2 ^{\frac {n}{2}} 22n种, 将所有可能用vetor存起来, 再枚举第二类的境况同理。
然后将第二类的所有可能按从小到大排序。
因为购买的商品总价格超过 x x x, 那么可以枚举第一类的所有方案, 假设第一类中第一个枚举的数是 y y y因为要大于 x x x, 所以第二类大于 x − y x - y x−y的都合法, 这个可以用二分算贡献。
所以总共的时间复杂度为 O ( 2 n 2 ∗ l o n g ( n ) ) O(2 ^ {\frac {n} {2}}* long(n)) O(22n∗long(n))
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 50;
typedef long long ll;
ll a[N], n, x;
vector<ll> first, second;
void dfs(int p, ll sum, int count) {
if (p > count) {
if (count == n / 2) {
first.push_back(sum);
} else {
second.push_back(sum);
}
return;
}
dfs(p + 1, sum + a[p], count);
dfs(p + 1, sum, count);
}
int main() {
cin >> n >> x;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
dfs(1, 0, n / 2);
dfs(n / 2 + 1, 0, n);
sort(second.begin(), second.end());
ll ans = 0;
for (int i = 0; i < first.size(); i++) {
ll value = first[i];
int p = lower_bound(second.begin(), second.end(), x - value) - second.begin();
if (p < second.size()) {
ans += 1ll*(second.size() - p);
}
}
cout << ans << endl;
}
F
考虑 dfs序建线段树, 对于操作2可以用线段树的区间修改, 对于操作3怎么办?
因为对于儿子结点dfs序不连续, 没法做到区间修改, 暴力修改?如果是菊花图,复杂度直接过不了。
因为操作1, 询问的是当前结点为根的子树所有点的权值和, 所以操作可以看成单点操作, 将当前询问点值乘上儿子节点个数,就可以了, 询问的时候再记录一个父亲节点已经加了多少。
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 7;
typedef long long ll;
vector<int> g[N];
ll a[N], n, q;
int id[N], cnt = 1, fa[N], sz[N], son[N];
ll tree[4 * N], flag[4 * N], sum[N];
void dfs(int u, int f) {
id[u] = cnt++;
fa[u] = f;
sz[u] = 1;
for (int to: g[u]) {
if (to == f) continue;
dfs(to, u);
son[u]++;
sz[u] += sz[to];
}
}
#define m (l + r) / 2
#define lson 2 * node
#define rson 2 * node + 1
void push_down(int node, int nl, int nr) {
if (flag[node]) {
tree[lson] += flag[node] * 1ll * nl;
tree[rson] += flag[node] * 1ll * nr;
flag[lson] += flag[node];
flag[rson] += flag[node];
flag[node] = 0;
}
}
void update(ll v, int ql, int qr, int l, int r, int node) {
if (ql <= l && qr >= r) {
tree[node] += 1ll * (r - l + 1) * v;
flag[node] += v;
return;
}
push_down(node, m - l + 1, r - m);
if (ql <= m) update(v, ql, qr, l, m, lson);
if (qr > m) update(v, ql, qr, m + 1, r, rson);
tree[node] = tree[lson] + tree[rson];
}
ll query(int ql, int qr, int l, int r, int node) {
if (ql <= l && qr >= r) {
return tree[node];
}
ll ans = 0;
push_down(node, m - l + 1, r - m);
if (ql <= m) ans += query(ql, qr, l, m, lson);
if (qr > m) ans += query(ql, qr, m + 1, r, rson);
return ans;
}
int main() {
ios::sync_with_stdio(0);
cin >> n >> q;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i < n; i++) {
int u, v; cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1, 0);
for (int i = 1; i <= n; i++) {
update(a[i], id[i], id[i], 1, n, 1);
}
while (q--) {
int op; cin >> op;
if (op == 1) {
int u; cin >> u;
cout << query(id[u], id[u] + sz[u] - 1, 1, n, 1) + sum[fa[u]] << endl;
} else if (op == 2) {
int u, v; cin >> u >> v;
update(v, id[u], id[u] + sz[u] - 1, 1, n, 1);
} else {
int u, v; cin >> u >> v;
sum[u] += v;
update(v * 1ll*(son[u] + 1), id[u], id[u], 1, n, 1);
}
}
}