首页 > 其他分享 >NOI 2023

NOI 2023

时间:2024-09-25 11:37:41浏览次数:1  
标签:NOI int sum back len 2023 include sim

Day1 T1 方格染色(color)

容易发现相对难处理的是斜线,但是我们发现斜线不超过 \(5\) 条,那么对于这一部分我们可以拆贡献,然后暴力做。

具体而言,先算出斜线减去横/竖线的面积,再算出横竖线面积并即可。

对于第一部分,考虑将斜线按照 \(y - x\) 分类,不同类斜线之间不会相互影响,同类斜线先去掉相交的情况,对于不交的斜线贡献也是独立的,对于它们再枚举横线和竖线,用 \(\text{set}\) 对相交点判重即可。

对于第二部分,我们只需考虑横线和竖线,考虑强化成任意矩形也是可做的,并且这是洛谷的扫描线板子,直线离散化 + 线段树即可。

时间复杂度 \(O(q \log q)\)。

Code
#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
#include <set>

using namespace std;
using LL = long long;

const int N = 2e5 + 5; 

int cid, n, m, q, t;
int p[N];

struct R {
  int type, x1, y1, x2, y2;
}; 

struct E {
  int x, l, r, v;
}; 

vector<R> vr;
map<int, vector<R>> mp; 
vector<E> ve;

namespace Seg {
  #define lc (k << 1)
  #define rc ((k << 1) + 1)

  const int T = N * 4; 

  int val[T], tag[T], sum[T];

  void Build (int k, int L = 1, int R = t) {
    if (L == R) {
      val[k] = p[L] - p[L - 1];
      return; 
    }
    int mid = (L + R) >> 1;
    Build(lc, L, mid);
    Build(rc, mid + 1, R);
    val[k] = val[lc] + val[rc]; 
  }

  void Add (int k, int l, int r, int x, int L = 1, int R = t) {
    if (l <= L && r >= R) {
      tag[k] += x;
    }
    else {
      int mid = (L + R) >> 1;
      if (l <= mid) {
        Add(lc, l, r, x, L, mid);
      }
      if (r > mid) {
        Add(rc, l, r, x, mid + 1, R);
      }
    }
    if (tag[k]) {
      sum[k] = val[k];
    }
    else {
      if (L == R)
        sum[k] = 0; 
      else
        sum[k] = sum[lc] + sum[rc];
    }
  }

  #undef lc
  #undef rc
}

int main () {
  // freopen("color.in", "r", stdin);
  // freopen("color.out", "w", stdout);
  cin.tie(0)->sync_with_stdio(0);
  cin >> cid >> n >> m >> q;
  for (int i = 1, t; i <= q; ++i) {
    R r;
    cin >> r.type >> r.x1 >> r.y1 >> r.x2 >> r.y2;
    if (r.type != 3)
      vr.push_back(r);
    else 
      mp[r.y1 - r.x1].push_back(r);
  }
  LL ans = 0; 
  for (auto i : mp)  {
    int del = i.first; 
    vector<R> v = i.second;
    sort(v.begin(), v.end(), [&](R a, R b) -> bool {
      return a.x1 < b.x1;
    });
    vector<R> tmp;
    for (auto j : v) {
      if (tmp.empty() || j.x1 > tmp.back().x2) {
        tmp.push_back(j);
      }
      else {
        tmp.back().x2 = max(tmp.back().x2, j.x2);
        tmp.back().y2 = tmp.back().x2 + del;
      }
    }
    for (auto j : tmp) {
      set<pair<int, int>> s;
      for (auto k : vr) {
        pair<int, int> p; 
        if (k.type == 1) {
          p = {k.y1 - del, k.y1};
        }
        else {
          p = {k.x1, k.x1 + del};
        }
        if (j.x1 <= p.first && p.first <= j.x2 && k.x1 <= p.first && p.first <= k.x2 && k.y1 <= p.second && p.second <= k.y2) {
          s.insert(p);
        }
      }
      ans += j.x2 - j.x1 + 1 - s.size();
    }
  }
  // cout << ans << '\n';
  for (auto i : vr) {
    p[++t] = i.y1 - 1, p[++t] = i.y2;
  }
  sort(p + 1, p + t + 1);
  t = unique(p + 1, p + t + 1) - p - 1;
  // for (int i = 1; i <= t; ++i) {
  //   cout << p[i] - p[i - 1] << ' ';
  // }
  // cout << '\n';
  for (auto &i : vr) {
    i.y1 = lower_bound(p + 1, p + t + 1, i.y1) - p;
    i.y2 = lower_bound(p + 1, p + t + 1, i.y2) - p; 
    // cout << i.x1 << ' ' << i.x2 << ' ' << i.y1 << ' ' << i.y2 << '\n';
    ve.push_back((E){i.x1, i.y1, i.y2, 1});
    ve.push_back((E){i.x2 + 1, i.y1, i.y2, -1});
  }
  Seg::Build(1);
  auto cmp = [&](E a, E b) -> bool {
    return a.x < b.x;
  }; 
  sort(ve.begin(), ve.end(), cmp);
  int lst = 0; 
  for (auto i : ve) {
    // cout << i.x << ' ' << i.l << ' ' << i.r << ' ' << i.v << ' ' << Seg::sum[1] << '\n';
    ans += 1ll * (i.x - lst) * Seg::sum[1];
    lst = i.x;
    Seg::Add(1, i.l, i.r, i.v);
  }
  cout << ans << '\n';
  return 0; 
}

Day1 T2 桂花树(tree)

考虑用虚树刻画 \(\text{LCA}\) 的限制,题目中的条件等价于:

  • 编号在 \([1, n]\) 中的点构成的虚树点集为其本身。

  • \(\forall i \in [n + 1, n + m]\),编号在 \([1, i]\) 中的点构成的虚树中,所有点的编号 \(\le i + k\)。

容易发现 \([1, n]\) 中点的相对位置肯定是不变的,考虑在此基础上添加 \(n + 1, n + 2, \ldots, n + m\)。

设 \(f_{i, S}\) 表示:已经在树上加入了 \([1, n + i]\) 中的所有结点,对于 \([n + i + 1, n + i + k]\) 中的每个位置 \(x\),我们用一个 \(01\) 变量表示,我们是否已在树上钦定了一个编号不超过 \(x\) 的点,状压成 \(S\),在这种情况下,方案数是多少。

转移有四种,下面令 \(c = n + i + \text{popcount}(S)\),表示当前树上的结点数:

  • 将 \(i + 1\) 挂在一个结点下面作为叶子,方案数为 \(c\)。

  • 将 \(i + 1\) 插入在某条边中,方案数为 \(c - 1\)。

  • 将 \(i + 1\) 填入到某个已经钦定的位置中,此时我们枚举 \(S\) 中所有为 \(1\) 的位置转移即可。

  • 对于某条边进行分裂,考虑一条树边 \((u, v)\),其中 \(u\) 为 \(v\) 的祖先,我们强制将 \(i + 1\) 挂在 \(v\) 旁边,并钦定一个结点 \(w\) 作为 \(i + 1\) 和 \(v\) 的 \(\text{LCA}\),此时我们要求 \(w \le i + k + 1\),这个限制可以加入到 \(S\) 中。对于每条边我们都可以这么做,方案数为 \(c - 1\)。

特别地,如果我们已经钦定了一个 \(\le i + 1\) 的点,那么第 \(1, 3, 4\) 种转移都是无效的,第 \(2\) 种转移也不能将 \(i + 1\) 填入其他位置中。

时间复杂度 \(O(mk2^k)\)。

Code
#include <iostream>

using namespace std;

const int M = 3005, S = (1 << 10);
const int Mod = 1e9 + 7; 

int n, m, k;
int f[M][S];

int main () {
  // freopen("tree.in", "r", stdin);
  // freopen("tree.out", "w", stdout);
  cin.tie(0)->sync_with_stdio(0);
  int cid, T;
  cin >> cid >> T;
  while (T--) {
    cin >> n >> m >> k; 
    for (int i = 1, f; i < n; cin >> f, ++i); 
    if (m == 0) {
      cout << 1 << '\n';
      continue;
    }
    f[0][0] = 1; 
    fill(f[1], f[m] + (1 << k), 0);
    for (int i = 0; i < m; ++i) {
      for (int s = 0; s < (1 << k); ++s) {
        int c = n + i + __builtin_popcount(s);
        if (!(s & 1)) 
          (f[i + 1][s >> 1] += f[i][s] * (c * 2 - 1ll) % Mod) % Mod;
        for (int j = 0, t; j < k; ++j) {
          if ((!(s & 1) || !j) && ((s >> j) & 1)) 
            t = (s >> 1) ^ (!j ? 0 : 1 << (j - 1)), (f[i + 1][t] += f[i][s]) %= Mod;
        }
        if (!(s & 1) && k)
          (f[i + 1][(s >> 1) | (1 << (k - 1))] += f[i][s] * (c - 1ll) % Mod) %= Mod;
      }
    }
    cout << f[m][0] << '\n';
  }
  return 0; 
}

Day2 T1 贸易(trade)

注意到向上走的路径只有一条,所以令 \(z = \text{LCA}(x, y)\),我们可以将 \(dis(x, y)\) 拆成 \(dis(x, z) + dis(z, y)\)。

比较麻烦的是在枚举 \(x, y\) 求和的时候我们要求 \(x\) 能到达 \(y\),首先观察到它等价于 \(z\) 能到达 \(y\),于是考虑枚举 \(y, z\),枚举量是 \(O(2^nn)\) 的,令 \(z\) 向非 \(y\) 祖先的 \(z\) 的儿子移动一条边后,得到的结点是 \(u\),那么合法的 \(x\) 形如 \(z\) 和 \(u\) 的子树(可以画个 \(\text{LCA}\) 图理解一下)。

首先看前一部分 \(dis(x, z)\),容易预处理一个结点 \(u\) 的子树内所有点到 \(u\) 的距离和 \(pre\),再设 \(siz\) 表示子树内结点个数,第一部分贡献显然就是 \(pre_u + a_u \times siz_u\)(考虑 \(u\) 子树内所有点须先走到 \(u\),再从 \(u\) 向上走一条边到达 \(z\))。

第二部分 \(dis(z, y)\),考虑枚举了 \(z, y\),这个值是固定的,那么只需计数有多少个 \(x\) 满足要求,而显然 \(x\) 的个数为 \(siz_u + 1\),所以我们现在的问题就变成了,对于每一个点,求每一个祖先到自己的最短路。

我们当然可以枚举一个起点。从它开始在它子树内跑最短路,这样入队次数是 \(O(2^nn)\) 的,复杂度是对的,但是考虑对于一对点 \(u, v\),\(u\) 是 \(v\) 的祖先,从 \(u\) 到 \(v\) 的最短路可能形如,先从 \(u\) 跳到某一个 \(u\) 的祖先 \(t\),从 \(t\) 走一条非树边到 \(s\),再从 \(s\) 到 \(v\),这种情况最短路会出 \(u\) 的子树。

注意到 \(s\) 一定是 \(u\) 子树内的点,并且从 \(t\) 跳到 \(s\) 后不会再向上走出 \(u\) 子树,否则没必要跳这条边,不如直接走出子树。所以我们枚举 \(u\) 子树内点 \(v\),我们要求的是 \(u\) 到 \(v\) 的最短路,我们枚举关于 \(v\) 的边 \(w \rightarrow v\),需要满足 \(w\) 在 \(u\) 子树外,那么存在一条 \(u \rightarrow w \rightarrow v\) 的路径,长度为 \(sum_u - sum_w + c(u, v)\),其中 \(sum\) 为树边前缀和,\(c(u, v)\) 表示 \(u\) 到 \(v\) 的边权,特殊处理即可。此外不存在最短路会出 \(u\) 子树的情况。

时间复杂度 \(O(2^nn^2)\)。

Code
#include <iostream>
#include <vector>
#include <cmath>
#include <queue>

#define lc (k << 1)
#define rc ((k << 1) | 1)

using namespace std;
using LL = long long;

const int L = 18, N = (1 << L) + 5;
const int Mod = 998244353;
const LL Inf = 1e18;

int n, m, ans;
int a[N], siz[N], pre[N], dep[N];
LL dis[N][L], sum[N];
vector<pair<int, int>> e[N], r[N];

void Dfs (int k) {
  sum[k] = sum[k >> 1] + a[k];
  siz[k] = 1; 
  if (lc < (1 << n)) {
    dep[lc] = dep[rc] = dep[k] + 1;
    Dfs(lc), Dfs(rc);
    siz[k] += siz[lc] + siz[rc];
    pre[k] = (pre[lc] + pre[rc] + 1ll * a[lc] * siz[lc] + 1ll * a[rc] * siz[rc]) % Mod;
  }
}

int main () {
  // freopen("trade.in", "r", stdin);
  // freopen("trade.out", "w", stdout);
  cin.tie(0)->sync_with_stdio(0);
  cin >> n >> m; 
  for (int i = 2; i < (1 << n); ++i) {
    cin >> a[i];
  }
  for (int i = 1, u, v, w; i <= m; ++i) {
    cin >> u >> v >> w;
    e[u].push_back({v, w});
    r[v].push_back({u, w});
  }
  Dfs(1);
  fill(dis[0], dis[(1 << n)] + L, Inf);
  for (int l = 0; l < n; ++l) {
    priority_queue<pair<LL, int>, vector<pair<LL, int>>, greater<pair<LL, int>>> q;
    for (int i = 1; i < (1 << n); ++i) {
      if (dep[i] == l)
        q.push({0, i});
    }
    for (int i = 1, j; i < (1 << n); ++i) {
      if (dep[i] <= l) continue;
      for (j = i; dep[j] > l; j >>= 1) {
      }
      for (auto k : r[i]) {
        int v = k.first, w = k.second;
        if (v < j)
          q.push(pair<LL, int>({sum[j] - sum[v] + w, i}));
      }
    }
    while (!q.empty()) {
      pair<LL, int> t = q.top();
      q.pop();
      int u = t.second;
      LL d = t.first;
      if (d < dis[u][l]) {
        dis[u][l] = d;
        if (dep[u] > l)
          q.push({d + a[u], u >> 1}); 
        for (auto i : e[u]) {
          int v = i.first, w = i.second;
          q.push({d + w, v});
        }
      }
    }
  }
  int ans = 0;
  for (int y = 1; y < (1 << n); ++y) {
    ans = (ans + pre[y]) % Mod;
    for (int z = y >> 1, d = dep[y] - 1; z && dis[y][d] != Inf; z >>= 1, --d) {
      int u = (((y >> (dep[y] - dep[z * 2])) == z * 2) ? z * 2 + 1 : z * 2);
      ans = (ans + pre[u] + 1ll * a[u] * siz[u]  + (dis[y][d] % Mod) * (siz[u] + 1)) % Mod;
    } 
  }
  cout << ans << '\n';
  return 0; 
}

Day2 T2 字符串(string)

下面记 \(s_{i \sim j}\) 表示 \(s\) 的第 \(i\) 个字符到第 \(j\) 个字符构成的子串,$< $ 表示比较字典序大小。

注意到 \(s_{i \sim {i + l - 1}} < s_{i + 2l - 1 \sim i + l}\) 的限制等价于 \(s_{i \sim {i + 2l - 1}} < s_{{i + 2l - 1} \sim i}\),也就是这个字符串正着读字典序比倒着读小。

继续放缩限制,考虑先计数 \(p = i + 2l - 1(1 \le l \le r)\) 的数量,满足 \(s_{i \sim n} < s_{p \sim 1}\),再减去不合法的部分,即 \(s_{i \sim p}\) 构成回文串,且 \(s_{i \sim n} > s_{p \sim 1}\)。

先考虑第一部分,注意到问题是前缀与后缀比较字典序大小的形式,所以构造 \(T = s + a + \overline{s} + b\),并对 \(T\) 后缀排序。其中 \(a\) 和 \(b\) 是任意小于字符集的字符,且满足 \(a < b\),这是模拟空串以避免不必要的麻烦。

由于合法的 \(p\) 构成一段区间中所有下标为奇数/偶数的位置,先奇偶分类,那么查询相当于求区间中 \(x\) 的个数,满足 \(rk_x > rk_i\),这个是二维数点,扫描线即可。

再考虑第二部分,由于 \(s_{i \sim p}\) 构成回文串,那么 \(s_{i \sim n} > s_{p \sim 1}\) 等价于考虑偶回文中心 \(t\) 和 \(t + 1\),满足 \(s_{t + 1 \sim n} > s_{t \sim 1}\)。

那么我们直接枚举回文中心 \(t, t + 1\),\(s_{t + 1 \sim n} > s_{t \sim 1}\) 的限制用前面的 SA 就可处理。但新增了两个限制,一是 \(s_{i \sim t} = s_{p \sim {t + 1}}\)(能构成回文串),二是从 \(i\) 开始的 \(r\) 个串中包含以 \(t, t + 1\) 为回文中心的串。

第一个限制我们可以先 Manacher 处理出 \(len_i\) 表示以 \(i, i + 1\) 为回文中心时的最长回文半径,那么若 \(i \in [t - len_t + 1, t]\) 则一定构成回文串。第二个限制相当于 \(i + r - 1 \ge t\),合起来又是一个二维偏序,还是扫描线即可。

时间复杂度 \(O(n \log n)\)。

Code
#include <iostream>
#include <algorithm>
#include <numeric>
#include <vector>

using namespace std;

const int N = 2e5 + 5; 

int n, q;
char s[N], t[N];
int x[N], r[N], ans[N], len[N];
int sa[N], rk[N], tmp[N], cnt[N], la[N * 2];

namespace Part1 {
  void Build_SA (int len) {
    iota(sa + 1, sa + len + 1, 1);
    sort(sa + 1, sa + len + 1, [&](int i, int j) -> bool {
      return s[i] < s[j];
    });
    rk[sa[1]] = 1;
    for (int i = 2; i <= len; ++i) {
      rk[sa[i]] = rk[sa[i - 1]] + (s[sa[i]] != s[sa[i - 1]]);
    }
    for (int l = 1; l < len; l <<= 1) {
      int t = 0; 
      for (int i = len - l + 1; i <= len; ++i) {
        tmp[++t] = i;
      }
      for (int i = 1; i <= len; ++i) {
        if (sa[i] > l) {
          tmp[++t] = sa[i] - l;
        } 
      }
      fill(cnt, cnt + len + 1, 0);
      for (int i = 1; i <= len; ++i) {
        ++cnt[rk[i]];
      }
      for (int i = 1; i <= len; ++i) {
        cnt[i] += cnt[i - 1];
      }
      for (int i = len; i; --i) {
        sa[cnt[rk[tmp[i]]]--] = tmp[i];
      }
      copy(rk + 1, rk + len + 1, la + 1);
      rk[sa[1]] = 1; 
      for (int i = 2; i <= len; ++i) {
        rk[sa[i]] = rk[sa[i - 1]] + (la[sa[i]] != la[sa[i - 1]] || la[sa[i] + l] != la[sa[i - 1] + l]);
      }
    }
  }

  int Irk (int x) { return n * 2 + 3 - rk[x]; }

  struct C {
    int p, v, o, x, id; 
  }; 

  struct BIT {
    int c[N * 2];

    void Clear () { fill(c, c + n * 2 + 2, 0); }

    void Add (int x, int y) {
      for (; x <= n * 2 + 2; x += (x & -x)) {
        c[x] += y; 
      }
    }

    int Query (int x) {
      int res = 0;
      for (; x; x -= (x & -x)) {
        res += c[x];
      }
      return res;
    }
  } b[2]; 

  void Main () {
    Build_SA(n * 2 + 2);
    vector<C> cv;
    for (int i = 1; i <= q; ++i) { 
      int x = ::x[i], r = ::r[i];
      int ql = n * 2 - x + 3 - r * 2, qr = n * 2 - x + 1; 
      if (ql > 1) cv.push_back(C({ql - 1, -1, (x ^ 1) & 1, Irk(x), i}));
      cv.push_back(C({qr, 1, (x ^ 1) & 1, Irk(x), i}));
    }
    sort(cv.begin(), cv.end(), [&](C a, C b) -> bool {
      return a.p < b.p;
    });
    b[0].Clear(), b[1].Clear();
    auto it = cv.begin();
    for (int i = 1; i <= n * 2 + 2; ++i) {
      b[i & 1].Add(Irk(i), 1);
      while (it != cv.end() && it->p == i) {
        ans[it->id] += it->v * b[it->o].Query(it->x - 1);
        ++it;
      }
    }
  }
}

namespace Part2 {
  void Manacher () {
    t[1] = '#';
    for (int i = 1; i <= n; ++i) {
      t[i * 2] = s[i];
      t[i * 2 + 1] = '#';
    } 
    fill(len + 1, len + n * 2 + 2, 0);
    for (int i = 1, d = 0, r = 0; i <= n * 2 + 1; ++i) {
      if (r >= i) {
        len[i] = min(len[d * 2 - i], r - i + 1);
      }
      while (i - len[i] > 0 && i + len[i] <= n * 2 + 1 && t[i - len[i]] == t[i + len[i]]) {
        ++len[i];
      }
      if (i + len[i] - 1 > r) {
        d = i, r = i + len[i] - 1; 
      }
    }
  }

  int Get_len (int i) { 
    return (len[i * 2 + 1] - 1) / 2;
  } 

  struct A {
    int p, x, v;
  };

  struct C {
    int p, x, id;
  }; 

  struct BIT {
    int c[N];

    void Clear () { fill(c, c + n + 1, 0); }

    void Add (int x, int y) {
      for (; x <= n; x += (x & -x)) {
        c[x] += y; 
      }
    }

    int Query (int x) {
      int res = 0; 
      for (; x; x -= (x & -x)) {
        res += c[x];
      }
      return res;
    }
  } b;

  void Main () {
    Manacher();
    vector<A> av;
    vector<C> cv;
    for (int i = 1; i < n; ++i) {
      if (rk[i + 1] >= rk[n * 2 + 2 - i]) continue;
      av.push_back(A({i - Get_len(i) + 1, i, 1}));
      av.push_back(A({i + 1, i, -1}));
    }
    for (int i = 1; i <= q; ++i) {
      cv.push_back(C({x[i], x[i] + r[i] - 1, i}));
    }
    sort(av.begin(), av.end(), [&](A a, A b) -> bool {
      return a.p < b.p;
    });
    sort(cv.begin(), cv.end(), [&](C a, C b) -> bool {
      return a.p < b.p;
    });
    auto ia = av.begin();
    auto ic = cv.begin();
    b.Clear();
    while (ia != av.end() || ic != cv.end()) {
      if (ia != av.end() && (ic == cv.end() || ia->p <= ic->p)) {
        b.Add(ia->x, ia->v), ++ia;
      }
      else {
        ans[ic->id] -= b.Query(ic->x), ++ic;
      }
    }
  }
}

int main () {
  cin.tie(0)->sync_with_stdio(0);
  int tid, T;
  cin >> tid >> T;
  while (T--) {
    cin >> n >> q;
    for (int i = 1; i <= n; ++i) {
      cin >> s[i];
    }
    copy(s + 1, s + n + 1, s + n + 2); 
    reverse(s + n + 2, s + n * 2 + 2);
    s[n + 1] = 'a' - 1, s[n * 2 + 2] = 'a' - 2;
    fill(ans + 1, ans + q + 1, 0);
    for (int i = 1; i <= q; ++i) {
      cin >> x[i] >> r[i];
    }
    Part1::Main();
    Part2::Main();
    for (int i = 1; i <= q; ++i) {
      cout << ans[i] << '\n';
    }
  }
  return 0;
}

标签:NOI,int,sum,back,len,2023,include,sim
From: https://www.cnblogs.com/hztmax0/p/18386292

相关文章

  • 算法题之图论 [NOIP2001 提高组] Car的旅行路线详细题解
    P1027[NOIP2001提高组]Car的旅行路线这道题的思路呢,就是建个图,然后跑一遍Floyd,比较最小值就可以解决了。but!它每个城市只给三个点(共四个),所以还得计算出第四个点坐标。这里根据矩形的中点公式来表示未知点的坐标:(这个思路源于大佬 _jimmywang_       ......
  • P5329 [SNOI2019] 字符串 题解
    Description给出一个长度为\(n\)的由小写字母组成的字符串\(a\),设其中第\(i\)个字符为\(a_i\(1\leqi\leqn)\)。设删掉第\(i\)个字符之后得到的字符串为\(s_i\),请按照字典序对\(s_1,s_2,……,s_n\)从小到大排序。若两个字符串相等,则认为编号小的字符串字典序更小。......
  • 2023CSP-J 普及组第二轮试题及解析( 第三题一元二次方程)
    参考程序代码:#include<bits/stdc++.h>usingnamespacestd;intt,m,a,b,c;intaa,bb,gd1,gd2;intgcd(inta,intb){ if(a%b==0)returnb; returngcd(b,a%b);}intmain(){ scanf("%d%d",&t,&m); while(t--) { scanf("%d%d%d"......
  • DOTS计算Voronoi图形生成,根据点自动划分区域生成多边形
      如图,生成Voronoi图形,代码如下。 usingUnityEngine;usingUnity.Mathematics;usingUnity.Jobs;usingUnity.Collections;usingUnity.Profiling;[ExecuteInEditMode]publicclassVoronoiTextureBurstJobComponent:MonoBehaviour{[SerializeField][Min(......
  • [COCI2022-2023#4] Zrinka
    [COCI2022-2023#4]Zrinka题意给定两个由\(0,1\)组成的序列。\(0\)只能填入偶数,\(1\)只能填入奇数。要求两个序列单调递增并且每个数最多使用一次。求所用数最大值的最小值。思路动态规划。定义\(dp_{i,j}\)表示序列\(1\)填到\(i\),序列\(2\)填到\(j\)的最小......
  • CCPC 2023 Final
    \(A.\)考虑合法的b序列长什么样,我们倒着做,把+变成-,在所有\(b_{i}>b_{i+1}\)的\(i\)操作\(b_{i}-b_{i+1}\)次前缀,后缀同理,最终要求b全部相等非负即满足条件。考虑前缀(后缀)操作本质是从某个地方开始后下降次数,那么我们设\(b_{0}=b_{n+1}=inf\),最终只需要判断\(\sum|b_{i}-b_{i+1}......
  • NOIP 模拟赛:2024-9-23
    打的算不错的了。就是C的部分分没时间打满了。T1签到题。记录\(pfx[],suf[]\)表示从前往后尽量少走、从后往前尽量多走,会走到哪里。然后枚举\(i=0\simm\),看\(pfx[i],suf[i+1]\)是否在同一个段内。T2码量题。记小边通向\(s_i\),大边通向\(l_i\)。部分分\(50\)分就......
  • bfs与优先队列 [NOIP2017 普及组] 棋盘————洛谷p3956
    [NOIP2017普及组]棋盘题目背景NOIP2017普及组T3题目描述有一个\(m\timesm\)的棋盘,棋盘上每一个格子可能是红色、黄色或没有任何颜色的。你现在要从棋盘的最左上角走到棋盘的最右下角。任何一个时刻,你所站在的位置必须是有颜色的(不能是无色的),你只能向上、下、左、右......
  • NOIP2024集训 Day37 总结
    前言今天的题目也是比较快速的做完了。所以先来总结一下。今天是计数专题,组合数居多。以前做过的题目这里就稍稍略过了。MergeTriplets观察到对于能够得到的最终的排列\(p\),对于其中的一个数\(p_i\),不可能做到\(p_i>\max_{j=i+1}^{i+3}p_j\)。感觉是比较显然的,这里就不......
  • MX-NOIP 2024 模拟 3.5
    赠的场次,质量却很高。#3.5T1交换连状压都打的复杂度超劣,真是水平下降严重。其实也基本想到了,前面一大部分贪心确定,后面的做部分分状压dp。设\(f_s\)表示填了\(s\)集合,最优的\(n'\),\(g_s\)表示此时对应的\(n\)。枚举最高位填哪个数,转移比较简单。往前换的最大代价......