2022牛客多校(六)
文章目录
一、比赛小结
比赛链接: "蔚来杯"2022牛客暑期多校训练营6
二、题目分析及解法(进阶题)
A、Array
题目链接:A-Array
题意:
给出序列 a i a_i ai ,满足: ∑ i = 1 n 1 a i ≤ 1 2 \displaystyle \sum_{i=1}^n \frac1{a_i}\leq \frac12 i=1∑nai1≤21 ,要求你构造出 c i , i = 0 , . . . , m − 1 c_i, i=0, ..., m-1 ci,i=0,...,m−1 。使得, c i c_i ci 所对应的无限序列 b i b_i bi ,有: 任意连续的 a i a_i ai 个元素中,一定存在一个元素为 i i i
题解:
考虑条件倒数和小于等于 1/2 怎么用,每个 a_i 改成小于他最大的 2 的幂,显然倒数和小于等于 1,按照哈夫曼树合并即可
代码:
#include <bits/stdc++.h>
using namespace std;
bool Mbe;
constexpr int N = 1 << 18;
int n, a[N], b[N];
pair<int, int> p[N];
bool Med;
int main() {
fprintf(stderr, "%.4lf MB\n", (&Mbe - &Med) / 1048576.0);
ios::sync_with_stdio(0);
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i], p[i] = make_pair(1 << 31 - __builtin_clz(a[i]), i);
sort(p + 1, p + n + 1);
int f = 0;
for (int i = 1; i <= n; i++) {
while (b[f]) f++;
for (int P = f; P < N; P += p[i].first) b[P] = p[i].second;
}
cout << N << "\n";
for (int i = 0; i < N; i++) cout << max(1, b[i]) << "\n";
cerr << 1e3 * clock() / CLOCKS_PER_SEC << " ms\n";
return 0;
}
B、Eezie and Pie
题目链接:B-Eezie and Pie
题意:
给定一棵有根树,每个节点可以为它的 0 ~ 𝑑[𝑖] 级祖先贡献 1 的价值。求最终每个点的价值。
题解:
树剖裸题
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 2e6 + 5;
int n;
int pre[maxn], deep[maxn], num[maxn], son[maxn]; // 第一次dfs的变量
int dfn[maxn], top[maxn], rk[maxn], id; // 第二次dfs的时候用到的变量
int val[maxn]; //, dfnval[maxn]; // 建树时用dfn序下的val
struct e {
int to, next;
} edge[maxn << 1];
int head[maxn], cnt;
void init() {
cnt = 1, id = 0;
memset(head, -1, sizeof(head));
for (int i = 0; i < maxn; i++) edge[i].next = -1;
}
struct segtree {
int l, r;
int val, add;
} t[maxn << 2];
void addedge(int u, int v) {
edge[cnt] = e{ v, head[u] };
head[u] = cnt++;
}
void predfs(int u, int fa, int d) {
deep[u] = d, num[u] = 1, pre[u] = fa;
for (int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].to;
if (v == fa) continue;
predfs(v, u, d + 1);
num[u] += num[v]; // 子树的节点数
if (num[son[u]] < num[v]) son[u] = v; // 重链子树的根节点
}
}
void dfs(int u, int t) {
dfn[u] = ++id, top[u] = t, rk[id] = u;
if (!son[u]) return; // 按重链顺序进行dfs
dfs(son[u], t);
for (int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].to;
if (v == pre[u] || v == son[u]) continue;
dfs(v, v); // 更新链首值top为v
}
}
void build(int root, int l, int r) {
t[root].l = l, t[root].r = r;
if (l == r) {
t[root].val = 0;
return;
}
int mid = (l + r) >> 1;
build(root * 2, l, mid);
build(root * 2 + 1, mid + 1, r);
t[root].val = (t[root * 2].val + t[root * 2 + 1].val);
}
void spread(int p) {
int l = t[p].l, r = t[p].r;
if (t[p].add) {
t[p * 2].add = (t[p * 2].add + t[p].add);
t[p * 2 + 1].add = (t[p * 2 + 1].add + t[p].add);
t[p * 2].val = (t[p * 2].val + (t[p * 2].r - t[p * 2].l + 1) * t[p].add);
t[p * 2 + 1].val =
(t[p * 2 + 1].val + (t[p * 2 + 1].r - t[p * 2 + 1].l + 1) * t[p].add);
t[p].add = 0;
}
}
void update(int root, int l, int r, int x) {
if (l <= t[root].l && t[root].r <= r) {
t[root].add = (t[root].add + x);
t[root].val = (t[root].val + (t[root].r - t[root].l + 1) * x);
return;
}
spread(root);
int mid = (t[root].r + t[root].l) >> 1;
if (l <= mid) update(root * 2, l, r, x);
if (mid < r) update(root * 2 + 1, l, r, x);
t[root].val = (t[root * 2].val + t[root * 2 + 1].val);
}
int query(int root, int l, int r) {
if (l <= t[root].l && t[root].r <= r) return t[root].val;
spread(root);
int res = 0;
int mid = (t[root].l + t[root].r) >> 1;
if (l <= mid) res = (res + query(root * 2, l, r));
if (mid < r) res = (res + query(root * 2 + 1, l, r));
return res;
}
void updatepath(int u, int v, int x) {
while (top[u] != top[v]) { // 更新不同链上的节点
if (deep[top[u]] < deep[top[v]]) swap(u, v);
update(1, dfn[top[u]], dfn[u], x);
u = pre[top[u]]; // 优先更新链首深度大的节点,更快迭代
}
if (deep[u] < deep[v]) swap(u, v); // 更新相同链上的节点
update(1, dfn[v], dfn[u], x);
}
int querypath(int u, int v) {
int res = 0;
while (top[u] != top[v]) { // 查询不同链上的节点
if (deep[top[u]] < deep[top[v]]) swap(u, v);
res = (res + query(1, dfn[top[u]], dfn[u]));
u = pre[top[u]]; // 优先查询链首深度大的节点,更快迭代
}
if (deep[u] < deep[v]) swap(u, v); // 查询相同链上的节点
res = (res + query(1, dfn[v], dfn[u]));
return res;
}
int find(int u, int d) {
// 查询u的第d个前驱节点
// d = 0 代表本身
int res = u;
int dfn1, dfn2;
while (d) {
dfn1 = dfn[res], dfn2 = dfn[top[res]];
if (dfn1 == dfn2) {
d--;
res = pre[res];
}
else if (d >= dfn1 - dfn2) {
d -= (dfn1 - dfn2);
res = top[res];
}
else {
res = rk[dfn1 - d];
d = 0;
}
if (res == 0) return res = 1;
}
return res;
}
signed main() {
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
init();
cin >> n;
for (int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
addedge(u, v), addedge(v, u);
}
for (int i = 1; i <= n; i++) cin >> val[i];
predfs(1, 0, 1);
dfs(1, 1);
build(1, 1, n);
for (int i = 1; i <= n; i++) {
int fi = find(i, val[i]);
updatepath(i, fi, 1);
}
for (int i = 1; i <= n; i++) cout << querypath(i, i) << " ";
cout << endl;
return 0;
}
G、Icon Design
题目链接:G-Icon Design
题意:
画图
题解:
按题意画图即可
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e2 + 5;
int n, cnt;
char res[maxn][maxn];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
int h = 4 * n + 5, w = 13 * n + 19;
for (int i = 1; i <= h; i++) {
for (int j = 1; j <= w; j++) {
if (i == 1 || j == 1 || i == h || j == w)
res[i][j] = '*';
else
res[i][j] = '.';
}
}
int x = n + 2, y = n + 3, len = 2 * n + 3;
for (int i = x; i <= x + len - 1; i++) {
for (int j = y; j <= y + len - 1; j++) {
if (i == j - 1 || j == y || j == y + len - 1) res[i][j] = '@';
}
}
y += len + n + 1;
for (int i = x; i <= x + len - 1; i++) {
for (int j = y; j <= y + len - 1; j++) {
if (i == x || i == x + n + 1 || j == y) res[i][j] = '@';
}
}
y += len + n + 1;
for (int i = x; i <= x + len - 1; i++) {
for (int j = y; j <= y + len - 1; j++) {
if (i == x + len - 1 || j == y) res[i][j] = '@';
}
}
y += len + n + 1;
for (int i = x; i <= x + len - 1; i++) {
for (int j = y; j <= y + len - 1; j++) {
if (i == x || i == x + n + 1 || i == x + len - 1 ||
(j == y && i <= x + n + 1) || (j == y + len - 1 && i >= x + n + 1))
res[i][j] = '@';
}
}
for (int i = 1; i <= h; i++) {
for (int j = 1; j <= w; j++) cout << res[i][j];
cout << endl;
}
return 0;
}
I、Line
题目链接:I-Line
题意:
给定 n n n 个向量,构造一个点集,使得点集中的任意一个点 ( a i , b i ) (a_i,b_i) (ai,bi) ,对于任意向量 ( x j , y j ) (x_j,y_j) (xj,yj) ,均有直线 ( a i + t x j , b i + t y j ) (a_i+tx_j,b_i+ty_j) (ai+txj,bi+tyj) 恰好经过 d d d 个点
1 ≤ n , d ≤ 6 , 0 ≤ x i , y i ≤ 6 , x i + y i > 0 1\leq n, d \leq 6, \ 0\leq x_i, y_i \leq 6, x_i + y_i >0 1≤n,d≤6, 0≤xi,yi≤6,xi+yi>0
题解:
数据范围很小,手玩题
•手玩 n=2 发现是平行四边形
•考虑更高维的情况
•发现平移复制 k 次
•本质是高维立方体在平面上的投影
代码:
#include <bits/stdc++.h>
using namespace std;
int n, k, x[11], y[11];
int main() {
cin >> n >> k;
set<pair<int, int> > all;
for (int i = 1; i <= n; i++) {
cin >> x[i] >> y[i];
int G = __gcd(x[i], y[i]);
x[i] /= G;
y[i] /= G;
all.insert(make_pair(x[i], y[i]));
}
n = 0;
for (auto p : all) {
n++;
x[n] = p.first;
y[n] = p.second;
}
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= n; j++) {
x[j] *= 37;
y[j] *= 37;
}
set<pair<int, int> > P;
P.insert(make_pair(0, 0));
for (int i = 1; i <= n; i++) {
set<pair<int, int> > NP;
for (auto p : P) {
for (int j = 0; j < k; j++) {
NP.insert(make_pair(p.first + j * x[i], p.second + j * y[i]));
}
}
P = NP;
}
cout << P.size() << endl;
for (auto p : P) cout << p.first << " " << p.second << endl;
return 0;
}
J、Number Game
题目链接:J-Number Game
题意:
给出一个三元数组 ( A , B , C ) (A, B, C) (A,B,C) ,可以执行如下操作:
B → A − B B \rarr A-B B→A−B 和 C → B − C C \rarr B-C C→B−C ,问 C C C 最后可以取到哪些至
题解:
其实这些操作转换为数学问题的话,其就是在做轴对称变换,两个轴对称组合意味着周期函数,进而这题就做完了
若两个操作使用次数相等,则 C 可以成为的集合 S={x∣x=C+k×(A-2×B)},若次数不等,则 S={x∣x=B-C+k×(A-2×B)},注意特判 A=2×B 的情况。
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
int a, b, c, x;
signed main() {
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int _;
cin >> _;
while (_--) {
cin >> a >> b >> c >> x;
bool flag = false;
// int T = (a - 2 * b) / 2;
int T2 = abs(a - 2 * b);
if (T2 == 0ll) {
if (x == c || x == b - c)
cout << "Yes\n";
else
cout << "No\n";
continue;
}
if (2ll * (x - (b - c)) % T2 == 0ll) flag = true;
if (2ll * (x - c) % T2 == 0ll) flag = true;
if (flag)
cout << "Yes\n";
else
cout << "No\n";
}
return 0;
}
M、Z-Game on grid
题目链接:M-Z-Game on grid
题意:
两个人在 N ∗ M N∗M N∗M 的网格上轮流移动一个棋子。棋子初始位为 (1,1) ,每次只能向某一维的正方向移动一格。网格上有一些特殊点,移到标 ’A’ 的点先手胜,移到标 ‘B’ 的点先手败,没有移到特殊点且不能再移动棋子则为平局。问先手是否有必胜、必平局、必败的策略。
题解:
简单的博弈,配合 dp 即可
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 600;
int n, m;
char chess[maxn][maxn];
char ans1[maxn][maxn], ans3[maxn][maxn], ans2[maxn][maxn];
int main() {
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int _;
cin >> _;
while (_--) {
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> chess[i] + 1;
for (int i = 1; i <= n; i++) {
strcpy(ans1[i] + 1, chess[i] + 1);
strcpy(ans2[i] + 1, chess[i] + 1);
strcpy(ans3[i] + 1, chess[i] + 1);
}
// win
if (ans1[n][m] == '.') ans1[n][m] = 'B';
for (int j = m - 1; j >= 1; j--) {
if (ans1[n][j] != '.') continue;
ans1[n][j] = ans1[n][j + 1];
}
for (int i = n - 1; i >= 1; i--) {
if (ans1[i][m] != '.') continue;
ans1[i][m] = ans1[i + 1][m];
}
for (int i = n - 1; i >= 1; i--)
for (int j = m - 1; j >= 1; j--) {
if (ans1[i][j] != '.') continue;
if ((i + j) % 2 == 0) { // A 抉择
if (ans1[i + 1][j] == 'B' && ans1[i][j + 1] == 'B')
ans1[i][j] = 'B';
else if (ans1[i + 1][j] == 'A' || ans1[i][j + 1] == 'A')
ans1[i][j] = 'A';
} else { // B 抉择
if (ans1[i + 1][j] == 'A' && ans1[i][j + 1] == 'A')
ans1[i][j] = 'A';
else if (ans1[i + 1][j] == 'B' || ans1[i][j + 1] == 'B')
ans1[i][j] = 'B';
}
}
// lose
if (ans2[n][m] == '.') ans2[n][m] = 'A';
for (int j = m - 1; j >= 1; j--) {
if (ans2[n][j] != '.') continue;
ans2[n][j] = ans2[n][j + 1];
}
for (int i = n - 1; i >= 1; i--) {
if (ans2[i][m] != '.') continue;
ans2[i][m] = ans2[i + 1][m];
}
for (int i = n - 1; i >= 1; i--)
for (int j = m - 1; j >= 1; j--) {
if (ans2[i][j] != '.') continue;
if ((i + j) % 2 == 0) { // A 抉择
if (ans2[i + 1][j] == 'A' && ans2[i][j + 1] == 'A')
ans2[i][j] = 'A';
else if (ans2[i + 1][j] == 'B' || ans2[i][j + 1] == 'B')
ans2[i][j] = 'B';
} else { // B 抉择
if (ans2[i + 1][j] == 'B' && ans2[i][j + 1] == 'B')
ans2[i][j] = 'B';
else if (ans2[i + 1][j] == 'A' || ans2[i][j + 1] == 'A')
ans2[i][j] = 'A';
}
}
// equal
for (int j = m - 1; j >= 1; j--) {
if (ans3[n][j] != '.') continue;
ans3[n][j] = ans3[n][j + 1];
}
for (int i = n - 1; i >= 1; i--) {
if (ans3[i][m] != '.') continue;
ans3[i][m] = ans3[i + 1][m];
}
for (int i = n - 1; i >= 1; i--)
for (int j = m - 1; j >= 1; j--) {
if (ans3[i][j] != '.') continue;
if ((i + j) % 2 == 0) { // A 抉择
if (ans3[i + 1][j] == '.' || ans3[i][j + 1] == '.')
ans3[i][j] = '.';
else
ans3[i][j] = 'A';
} else { // B 抉择
if (ans3[i + 1][j] == '.' && ans3[i][j + 1] == '.')
ans3[i][j] = '.';
else
ans3[i][j] = 'A';
}
}
if (ans1[1][1] == 'A')
cout << "yes ";
else
cout << "no ";
if (ans3[1][1] == '.')
cout << "yes ";
else
cout << "no ";
if (ans2[1][1] == 'B')
cout << "yes" << endl;
else
cout << "no" << endl;
// for (int i = 1; i <= n; i++) cout << ans1[i] + 1 << endl;
// for (int i = 1; i <= n; i++) cout << ans2[i] + 1 << endl;
// for (int i = 1; i <= n; i++) cout << ans3[i] + 1 << endl;
}
return 0;
}
三、题目分析及解法(进阶题)
不会做X