2022牛客多校(六)

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=1nai121 ,要求你构造出 c i , i = 0 , . . . , m − 1 c_i, i=0, ..., m-1 ci,i=0,...,m1 。使得, 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 1n,d6, 0xi,yi6,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 BAB C → B − C C \rarr B-C CBC ,问 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 NM 的网格上轮流移动一个棋子。棋子初始位为 (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

C、Forest

D、Fourier and Theory for the Universe

E、From AtCoder

F、Hash

H、Jumping Steps

K、SolarPea and Inversion

L、Striking String Problem

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值