EC Final 2019 题解

有两道题没做

A. City

直接考虑中点即可。直接枚举复杂度 Θ ( n m ) \Theta(nm) Θ(nm)。(但是也可以化简式子做到 Θ ( 1 ) \Theta(1) Θ(1)

#include <bits/stdc++.h>

#define LOG(FMT...) fprintf(stderr, FMT)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> vi;

int main() {
#ifdef LBT
  freopen("test.in", "r", stdin);
  int nol_cl = clock();
#endif
  ios::sync_with_stdio(false);
  cin.tie(nullptr);

  int n, m;
  cin >> n >> m;
  ll ans = 0;
  for (int i = 0; i <= n; ++i)
    for (int j = 0; j <= m; ++j)
      ans += (min(i, n - i) * 2 + 1) * (min(j, m - j) * 2 + 1) / 2;
  cout << ans;

#ifdef LBT
  LOG("Time: %dms\n", int ((clock()
      -nol_cl) / (double)CLOCKS_PER_SEC * 1000));
#endif
  return 0;
}

B. Black and White

考虑如此构造:对于一个变量 v v v,在第奇数步向右走就 + 1 +1 +1 否则 − 1 -1 1,偶数步倒过来,那么 v ∈ [ 4 k − 2 , 4 k + 1 ] v \in [4k - 2, 4k + 1] v[4k2,4k+1],那么对于给定 ( n , m , v ) (n, m, v) (n,m,v) 的方案数用组合数就可以表示。事实上可以做到 Θ ( T + n + m ) \Theta(T + n + m) Θ(T+n+m),但是本代码不想判奇偶性,是 Θ ( T ( n + m ) ) \Theta(T(n+m)) Θ(T(n+m)) 的实现。

#include <bits/stdc++.h>

#define LOG(FMT...) fprintf(stderr, FMT)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int N = 100010, P = 998244353;

int fac[N * 2], ifac[N * 2];

int norm(int x) { return x >= P ? x - P : x; }

int binom(int n, int m) { return fac[n] * (ll)ifac[m] % P * ifac[n - m] % P; }

void prepare(int n) {
  fac[0] = 1;
  for (int i = 1; i <= n; ++i) fac[i] = fac[i - 1] * (ll)i % P;
  ifac[0] = ifac[1] = 1;
  for (int i = 2; i <= n; ++i) ifac[i] = -(P / i) * (ll)ifac[P % i] % P + P;
  for (int i = 1; i <= n; ++i) ifac[i] = ifac[i] * (ll)ifac[i - 1] % P;
}

int calc(int n, int m, int k) {
  k += n + m;
  if (k & 1) return 0;
  k /= 2;
  for (int i = 0; i <= (n + m + 1) / 2; ++i) {
    int x = i + ((n + m) / 2 - k + i), y = (n + m + 1) / 2 - i + k - i;
    if ((n + m) / 2 - k + i >= 0 && (k - i) >= 0)
    if (x == n && y == m)
      return binom((n + m + 1) / 2, i) * (ll)binom((n + m) / 2, k - i) % P;
  }
  return 0;
}

int main() {
#ifdef LBT
  freopen("test.in", "r", stdin);
  int nol_cl = clock();
#endif
  ios::sync_with_stdio(false);
  cin.tie(nullptr);

  prepare(200000);
  int t;
  cin >> t;
  while (t--) {
    int n, m, k;
    cin >> m >> n >> k;
    cout << norm(norm(calc(n, m, 4 * k - 2) + calc(n, m, 4 * k - 1)) + norm(calc(n, m, 4 * k) + calc(n, m, 4 * k + 1))) << '\n';
  }

#ifdef LBT
  LOG("Time: %dms\n", int ((clock()
          -nol_cl) / (double)CLOCKS_PER_SEC * 1000));
#endif
  return 0;
}

C. Dirichlet k k k-th root

考虑 k k k 次根就是 exp ⁡ ln ⁡ f k \exp \frac{\ln f}k expklnf由于狄利克雷卷积在 f ( 1 ) = 0 f(1)=0 f(1)=0 的乘方超过 ⌊ log ⁡ 2 n ⌋ \lfloor \log_2 n\rfloor log2n 次就会变成 0 0 0,那么直接暴力计算即可 Θ ( n log ⁡ 2 n ) \Theta(n\log ^2 n) Θ(nlog2n) 解决。
可以考虑递推式 g ′ f = 1 k f ′ g g'f=\frac1k f'g gf=k1fg
idea from _rqy: 考虑任意满足和微分相同的算子 Ω \Omega Ω 都能通过运算律得出 ( Ω g ) f = 1 k ( Ω f ) g (\Omega g)f = \frac1k (\Omega f) g (Ωg)f=k1(Ωf)g,因此取 Ω D f : f n → ω ( n ) f n \Omega \mathfrak D_f: f_n \rightarrow \omega(n)f_n ΩDf:fnω(n)fn,其中 ω ( n ) \omega(n) ω(n) 是可重质因子数量。这个好在他保留 1 1 1 项以外全部信息,可以直接用于算出所有项。时间复杂度 Θ ( n log ⁡ n ) \Theta(n\log n) Θ(nlogn)

#include <bits/stdc++.h>

#define LOG(FMT...) fprintf(stderr, FMT)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int N = 100010, L = 30, P = 998244353;

bool vis[N];
int pk[N], nv[L];

int norm(int x) { return x >= P ? x - P : x; }

void add(int& x, int y) {
  if ((x += y) >= P) x -= P;
}

void sub(int& x, int y) {
  if ((x -= y) < 0) x += P;
}

void exGcd(int a, int b, int& x, int& y) {
  if (!b) {
    x = 1;
    y = 0;
    return;
  }
  exGcd(b, a % b, y, x);
  y -= a / b * x;
}

int inv(int a) {
  int x, y;
  exGcd(a, P, x, y);
  return norm(x + P);
}

vector<int> Power(const vector<int>& f, int k) {
  int n = (int)f.size() - 1;
  vector<int> g(n + 1), vf = f;
  for (int i = 1; i <= n; ++i) vf[i] = f[i] * (ll)pk[i] % P * k % P;
  for (int i = 1; i <= n; ++i) {
    int v = g[i];
    g[i] = g[i] * (ll)nv[pk[i]] % P + (i == 1);
    for (int j = 2, t = i + i; t <= n; t += i, ++j)
      g[t] = (g[t] + g[i] * (ll)vf[j]) % P;
    for (int j = 2, t = i + i; t <= n; t += i, ++j)
      g[t] = (g[t] + (P - f[j]) * (ll)v) % P;
  }
  return g;
}

int main() {
#ifdef LBT
  freopen("test.in", "r", stdin);
  int nol_cl = clock();
#endif

  int n, k;
  scanf("%d%d", &n, &k);
  vector<int> a(n + 1);
  for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
  for (int i = 2; i <= n; ++i)
    if (!vis[i]) {
      for (int j = i; j <= n; j += i) vis[j] = true;
      for (ll x = i; x <= n; x *= i)
        for (int j = x; j <= n; j += x)
          ++pk[j];
    }
  nv[1] = 1;
  for (int i = 2; i < L; ++i) nv[i] = -(P / i) * (ll)nv[P % i] % P + P;

  a = Power(a, inv(k));
  for (int i = 1; i <= n; ++i) printf("%d%c", a[i], " \n"[i == n]);

#ifdef LBT
  LOG("Time: %dms\n", int ((clock()
      -nol_cl) / (double)CLOCKS_PER_SEC * 1000));
#endif
  return 0;
}

D. Fire

注意到限制非常紧,一定是一个欧拉回路但是可以少走最后一小段,记 x ( u ) x(u) x(u) 如果 u u u 的子树要走一整遍的可允许的最晚到达 u u u 的时间, y ( u ) y(u) y(u) 如果 u u u 遍历完但是可以停在里面的最晚允许时间。
注意到按照 x ( v ) + 2 s i z e ( v ) x(v) + 2size(v) x(v)+2size(v) 的大小排序遍历子树就是使得 x x x 最优的方案,那么为了计算 y y y 需要枚举走到哪个子树里,这个只需对之前的最值用前后缀处理一下就可以快速计算。算出来的答案 < 0 < 0 <0 就无解。
总共就是一次 DFS,时间复杂度 Θ ( n log ⁡ n ) \Theta(n\log n) Θ(nlogn)

#include <bits/stdc++.h>

#define LOG(FMT...) fprintf(stderr, FMT)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> vi;

const int N = 100010;

int n, k;
int a[N], s[N];
ll x[N], y[N];
vector<int> g[N];
vector<pair<pair<ll, int>, int>> greedy[N];

void dfs(int u) {
  s[u] = 1;
  for (int v : g[u])
    if (!s[v]) {
      dfs(v);
      s[u] += s[v];
      greedy[u].emplace_back(make_pair(x[v] - 1, s[v] * 2), v);
    }
  sort(greedy[u].begin(), greedy[u].end(), [&](const pair<pair<ll, int>, int>& lhs, const pair<pair<ll, int>, int>& rhs) { return rhs.first.first - lhs.first.second > lhs.first.first - rhs.first.second; });
  int d = greedy[u].size();
  vector<ll> dp;
  dp.push_back(numeric_limits<ll>::max());
  int pre = 0;
  x[u] = a[u] + min(k - 2 * s[u], 0);
  for (const auto& pr : greedy[u]) {
    x[u] = min(x[u], pr.first.first - pre);
    dp.push_back(min(dp.back(), pr.first.first - pre));
    pre += pr.first.second;
  }
  if (d) {
    vector<ll> suf(d + 1);
    suf[d] = numeric_limits<ll>::max();
    for (int i = d - 1; i >= 0; --i)
      suf[i] = min(suf[i + 1] - greedy[u][i].first.second, greedy[u][i].first.first);
    y[u] = numeric_limits<ll>::min();
    pre = 0;
    for (int i = 0; i < d; ++i) {
      int v = greedy[u][i].second;
      y[u] = max(y[u], min({y[v] - 2 * (s[u] - s[v] - 1) - 1, suf[i + 1] - pre, dp[i], (ll)a[u], a[u] + k - 2LL * (s[u] - s[v])}));
      pre += greedy[u][i].first.second;
    }
  } else
    y[u] = a[u];
}

int main() {
#ifdef LBT
  freopen("test.in", "r", stdin);
  int nol_cl = clock();
#endif
  ios::sync_with_stdio(false);
  cin.tie(nullptr);

  cin >> n >> k;
  for (int rep = 1; rep < n; ++rep) {
    int u, v;
    cin >> u >> v;
    g[u].push_back(v);
    g[v].push_back(u);
  }
  for (int i = 1; i <= n; ++i) cin >> a[i];
  dfs(1);
  cout << max(-1LL, y[1]) << '\n';

#ifdef LBT
  LOG("Time: %dms\n", int ((clock()
          -nol_cl) / (double)CLOCKS_PER_SEC * 1000));
#endif
  return 0;
}

E. Flow

可以发现图的形态就是 1 1 1 走到 n n n 的若干条等长链,最大流显然取到 ⌊ ∑ z k ⌋ \lfloor \frac {\sum z} k \rfloor kz,我们需要让每条链的最小值之和达到这个值。考虑每条链最小值的调整代价是凸的,将每条链上的边权排序之后将支付费用从小到大支付即可。复杂度 Θ ( n log ⁡ n ) \Theta(n\log n) Θ(nlogn)

#include <bits/stdc++.h>

#define LOG(FMT...) fprintf(stderr, FMT)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int N = 100010;

int n, m;
int s[N], nxt[N], nxtz[N], a[N];
ll support[N];

int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);

  cin >> n >> m;
  int d = 0;
  ll sum = 0;
  memset(s, -1, sizeof(s));
  for (int i = 0; i < m; ++i) {
    int x, y, z;
    cin >> x >> y >> z;
    if (x == 1) {
      ++d;
      s[y] = z;
    } else {
      nxt[x] = y;
      nxtz[x] = z;
    }
    sum += z;
  }
  int p = m / d;
  ll f = sum / p;
  for (int i = 1; i <= n; ++i)
    if (s[i] != -1) {
      int k = 0;
      a[++k] = s[i];
      int u = i;
      while (u != n) {
        a[++k] = nxtz[u];
        u = nxt[u];
      }
      sort(a + 1, a + k + 1);
      for (int j = 1; j <= k; ++j)
        support[j - 1] += a[j] - a[j - 1];
    }
  ll ans = 0, tot = support[0];
  int ptr = 0;
  while (tot + support[ptr + 1] < f) {
    tot += support[++ptr];
    ans += support[ptr] * ptr;
  }
  ++ptr;
  ans += ptr * (f - tot);
  cout << ans;

  return 0;
}

F. Game

非常玄学,虽然保证数据随机,然而 shuffle 会挂,但是随便爬爬山就能过,目标函数灵敏的话效果较好,比如设置成任意一种 swap 方式的剩余对方棋子数量之和。

#include <bits/stdc++.h>

#define LOG(FMT...) fprintf(stderr, FMT)

using namespace std;

const int N = 24;

bool ok;
int a[N], b[N] = {40, 39, 38, 38, 37, 37, 36, 36, 35, 35, 34, 34, 34, 33, 33, 33, 32, 32, 32, 31, 31, 31, 30, 30};
pair<int, pair<int, int>> good[N * N];

int check() {
  int x = 0, y = 0;
  while (x < N && y < N) {
    if (a[x] == b[y] || a[x] == 30 || b[y] == 30) {
      ++x;
      ++y;
    } else if (a[x] == 31)
      ++(b[y] == 32 ? x : y);
    else if (b[y] == 31)
      ++(a[x] == 32 ? y : x);
    else if (a[x] > b[y])
      ++y;
    else
      ++x;
  }
  ok &= y != N;
  return x;
}

int calc() {
  ok = true;
  int ret = 0;
  for (int i = 0; i < N; ++i)
    for (int j = i + 1; j < N; ++j) {
      swap(b[i], b[j]);
      ret += check();
      swap(b[i], b[j]);
    }
  return ret;
}

int main() {
#ifdef LBT
  freopen("test.in", "r", stdin);
  int nol_cl = clock();
#endif
  ios::sync_with_stdio(false);
  cin.tie(nullptr);

  mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
  uniform_int_distribution<int> uid(0, N - 1);

  int t;
  cin >> t;
  while (t--) {
    for (int i = 0; i < N; ++i) cin >> a[i];
    int cur;
    while ((cur = calc()) < 22 * 23 / 2 * 24) shuffle(b, b + N, rng);
    while (!ok) {
      int i = uid(rng), j = uid(rng);
      swap(b[i], b[j]);
      int tmp = calc();
      if (tmp > cur || ((tmp == cur) && (rng() & 1)) || ok)
        cur = tmp;
      else
        swap(b[i], b[j]);
    }
    for (int i = 0; i < N; ++i) cout << b[i] << ' ';
    cout << '\n';
  }

#ifdef LBT
  LOG("Time: %dms\n", int ((clock()
      -nol_cl) / (double)CLOCKS_PER_SEC * 1000));
#endif
  return 0;
}

G. Happiness

模拟。卡常

#include <bits/stdc++.h>

#define LOG(FMT...) fprintf(stderr, FMT)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> vi;

const int T = 10, N = 310;

int n1, n3, n6;
int n, smallest, largest, ans;
int px[N], py[N], ct[N], fst[N];
bool vis[N], eq[N];
char s[1000];
int ctm;
vi teams[N];

int pivot[N];

bool cmp(int *p1, int *p2, int k) {
  while (k--) {
    if (*p1 != *p2) return *p1 < *p2;
    ++p1;
    ++p2;
  }
  return true;
}

void dfs(int k, int penalty, int tm, int rank) {
  {
    int ms = numeric_limits<int>::max(), ml = -2;
    if (k) {
      ms = pivot[2];
      ml = pivot[k + 1];
    }
    reverse(pivot + 2, pivot + k + 2);
    while (eq[rank - 1] || rank > 1 && (pivot[0] < teams[rank - 1][0] || (pivot[0] == teams[rank - 1][0] &&
                                                                          cmp(pivot + 1,
                                                                              teams[rank - 1].begin().base() + 1,
                                                                              k + 1))))
      --rank;
    reverse(pivot + 2, pivot + k + 2);
    int cur = 5000 / rank;
    if (rank <= n6) cur += 400;
    if (rank <= n3) cur += 400;
    if (rank <= n1) cur += 400;
    if (ms <= smallest) cur += 700;
    if (ml >= largest) cur += 500;
    for (int i = 0; i < T; ++i) if (vis[i] && ct[i] <= fst[i]) cur += 800;
    ans = max(ans, cur);
  }

  for (int i = 0; i < T; ++i)
    if (!vis[i] && px[i] != -1 && tm + px[i] <= 300) {
      pivot[k + 2] = ct[i] = tm + px[i];
      pivot[1] = penalty + ct[i] + py[i] * 20;
      pivot[0] = -k - 1;
      vis[i] = true;
      dfs(k + 1, pivot[1], ct[i], rank);
      vis[i] = false;
      pivot[1] = penalty;
      pivot[0] = -k;
    }
}

int main() {
#ifdef LBT
  freopen("test.in", "r", stdin);
  int nol_cl = clock();
#endif
  ios::sync_with_stdio(false);
  cin.tie(nullptr);

  ctm = clock();

  cin >> n;
  cin.ignore();
  smallest = numeric_limits<int>::max() - 1;
  largest = -1;
  fill(fst, fst + T, numeric_limits<int>::max());
  for (int i = 1; i < n; ++i) {
    cin.getline(s, 1000);
    int p = 0, k = 0, penalty = 0;
    vi sol;
    for (int j = 0; j < T; ++j)
      if (s[p] == '-') {
        p += 2;
      } else {
        int x, y;
        sscanf(s + p, "%d%d", &x, &y);
        while (s[p] != ',' && s[p] != 0) ++p;
        ++p;
        penalty += x + y * 20;
        ++k;
        sol.push_back(x);
        smallest = min(smallest, x);
        largest = max(largest, x);
        fst[j] = min(fst[j], x);
      }
    sort(sol.begin(), sol.end(), greater<int>());
    teams[i].push_back(-k);
    teams[i].push_back(penalty);
    teams[i].insert(teams[i].end(), sol.begin(), sol.end());
  }
  cin.getline(s, 1000);
  int p = 0;
  for (int j = 0; j < T; ++j)
    if (s[p] == '-') {
      px[j] = -1;
      p += 2;
    } else {
      int x, y;
      sscanf(s + p, "%d%d", &x, &y);
      while (s[p] != ',' && s[p] != 0) ++p;
      ++p;
      px[j] = x;
      py[j] = y;
    }
  sort(teams + 1, teams + n);
  n1 = n / 10;
  n3 = n1 * 3;
  n6 = n1 * 6;
  for (int i = 1; i < n; ++i)
    if (teams[i] == teams[i + 1]) eq[i] = true;
  dfs(0, 0, 0, n);
  cout << ans;

#ifdef LBT
  LOG("Time: %dms\n", int ((clock()
          -nol_cl) / (double)CLOCKS_PER_SEC * 1000));
#endif
  return 0;
}

H. King

考虑进行随机化,如果我们每次随机选取一个位置 i i i,那么 i i i 在答案序列中且 i i i 的下一个被取的位置是 i + k i + k i+k k ≤ 2 k\le 2 k2 的概率至少是 1 4 \frac 1 4 41,因此我们随机 λ \lambda λ 次就有至少 1 − 0.7 5 λ 1 - 0.75^\lambda 10.75λ 的概率得到正确答案,取 λ = 50 \lambda = 50 λ=50 足够通过。时间复杂度 Θ ( λ n ) \Theta(\lambda n) Θ(λn)

#include <bits/stdc++.h>

#define LOG(FMT...) fprintf(stderr, FMT)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> vi;

const int N = 200010;

int n, p;
int a[N];

int main() {
#ifdef LBT
  freopen("test.in", "r", stdin);
  int nol_cl = clock();
#endif

  mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

  int t;
  scanf("%d", &t);
  while (t--) {
    scanf("%d%d", &n, &p);
    uniform_int_distribution<int> uid(1, n - 1);
    for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    int ans = -1;
    int rep = 50;
    while (rep--) {
      int i = uid(rng);
      for (int t = 1; t <= min(2, n - i); ++t) {
        int cur = 2;
        int x = a[i], y = a[i + t];
        for (int j = i - 1; j; --j)
          if (x * (ll)x % p == a[j] * (ll)y % p) {
            y = x;
            x = a[j];
            ++cur;
          }
        x = a[i]; y = a[i + t];
        for (int j = i + t + 1; j <= n; ++j)
          if (y * (ll)y % p == a[j] * (ll)x % p) {
            x = y;
            y = a[j];
            ++cur;
          }
        if (cur * 2 >= n) ans = max(ans, cur);
      }
    }
    printf("%d\n", ans);
  }

#ifdef LBT
  LOG("Time: %dms\n", int ((clock()
      -nol_cl) / (double)CLOCKS_PER_SEC * 1000));
#endif
  return 0;
}

I. Moon

计算几何,不做

J. Permutation

考虑 1 1 1 的位置,显然他左边的数不可能到右边去,如果它左边有 c c c 个数,那么可以认为他左边那 c c c 的长度内的数可以任意排列,考虑规划成子问题就是一段区间已知最左边 x c xc xc 个数是“自由”的,最右边 y c yc yc 个数是“自由”的,每次段内找最小值能否扩展即可。但是注意用于交换的数有一个更紧的位置限制,所以不是直接乘以阶乘。用线段树每次把当前找到最小的位置赋值成 + ∞ +\infty +,接着找次小的数如此下去。时间复杂度 Θ ( n log ⁡ n ) \Theta(n\log n) Θ(nlogn)

#include <bits/stdc++.h>

#define LOG(FMT...) fprintf(stderr, FMT)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

struct Node {
  int l, r;
  int v;
  Node *ls, *rs;
  void upd() { v = min(ls->v, rs->v); }
  void ch(int k, int x) {
    if (l == r) {
      v = x;
      return;
    }
    (k <= ls->r ? ls : rs)->ch(k, x);
    upd();
  }
  int qry(int l, int r) const {
    if (this->l == l && this->r == r) return v;
    if (r <= ls->r) return ls->qry(l, r);
    if (l >= rs->l) return rs->qry(l, r);
    return min(ls->qry(l, ls->r), rs->qry(rs->l, r));
  }
};

const int N = 500010, P = 998244353;

int n, c, ans;
int a[N], pos[N], fac[N];
Node* segTree;

Node* build(int l, int r) {
  static Node pool[N * 2], *top = pool;
  Node* p = top++;
  p->l = l;
  p->r = r;
  if (l == r) {
    p->v = a[l];
    return p;
  }
  int mid = (l + r) >> 1;
  p->ls = build(l, mid);
  p->rs = build(mid + 1, r);
  p->upd();
  return p;
}

void solve(int l, int r, int x, int y) {
  while (true) {
    if (r - l + 1 < (x + y) * c || l > r) {
      ans = ans * (ll) fac[r - l + 1 - max(0, x - 1) - max(0, y - 1)] % P;
      return;
    }
    int v = segTree->qry(l, r), p = pos[v];
    if (p < l + x * c) {
      ans = ans * (ll)(c * x - (x - 1)) % P;
      ++x;
      segTree->ch(p, n + 1);
    } else if (p > r - y * c) {
      ans = ans * (ll)(c * y - (y - 1)) % P;
      ++y;
      segTree->ch(p, n + 1);
    } else {
      solve(l, p - 1, x, (p - l) >= c);
      solve(p + 1, r, (r - p) >= c, y);
      return;
    }
  }
}

int main() {
#ifdef LBT
  freopen("test.in", "r", stdin);
  int nol_cl = clock();
#endif

  fac[0] = 1;
  for (int i = 1; i < N; ++i) fac[i] = fac[i - 1] * (ll)i % P;
  int t;
  scanf("%d", &t);
  while (t--) {
    scanf("%d%d", &n, &c);
    for (int i = 1; i <= n; ++i) {
      scanf("%d", &a[i]);
      pos[a[i]] = i;
    }
    segTree = build(1, n);
    ans = 1;
    solve(1, n, 0, 0);
    printf("%d\n", ans);
  }

#ifdef LBT
  LOG("Time: %dms\n", int ((clock()
      -nol_cl) / (double)CLOCKS_PER_SEC * 1000));
#endif
  return 0;
}

K. All Pair Maximum Flow

首先最小割转平面图最短路,由于这个图是一个剖分所以没有环,一对最大流就是把叶子节点环形排成一排编号,每个 [ l , r ] ( r < n ) [l, r] (r < n) [l,r](r<n) 区间内节点和外节点的距离最小值。又考虑对于图 w ( u , v ) = w(u, v) = w(u,v)= 在树上的距离,那么一个割边必然在新图的 MST 上,我们用 DP 完成 Boruvka 过程可以算出这个 MST,然后考虑把 MST 从小到大往里加,维护一个扫描线可以求出所有答案的和。

L. Travel

码农题,不做

M. Value

注意到每个 i k i^k ik 是互不相交的,也就是说我们考虑对每个 i i i 不能被表示为更小的数的幂,对 i i i 的幂对应的数枚举子集计算结果。这一复杂度必然不超过
Θ ( ( ∑ x = 2 n 2 ⌊ log ⁡ x n ⌋ ) log ⁡ n log ⁡ log ⁡ n ) \Theta \left(\left(\sum_{x = 2}^n 2^{\lfloor\log_x n\rfloor}\right)\log n\log \log n\right) Θ((x=2n2logxn)lognloglogn)
而经过近似, n = 1 0 5 n=10^5 n=105 时该求和不超过 3 × 1 0 5 3\times 10^5 3×105,故完全可以通过。猜测复杂度可以被分析为 Θ ( n log ⁡ n log ⁡ log ⁡ n ) \Theta(n\log n\log \log n) Θ(nlognloglogn)

#include <bits/stdc++.h>

#define LOG(FMT...) fprintf(stderr, FMT)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> vi;

const int N = 100010, L = 19;
int a[N], b[N];
int ma[L], mb[L];
bool vis[N];

int main() {
#ifdef LBT
  freopen("test.in", "r", stdin);
  int nol_cl = clock();
#endif
  ios::sync_with_stdio(false);
  cin.tie(nullptr);

  int n;
  cin >> n;
  for (int i = 1; i <= n; ++i) cin >> a[i];
  for (int i = 1; i <= n; ++i) cin >> b[i];
  ll ans = a[1];
  for (int i = 2; i <= n; ++i) {
    if (!vis[i]) {
      ll cur = 0;
      int cnt = 0;
      for (ll x = i; x <= n; x *= i) {
        vis[x] = true;
        ++cnt;
        ma[cnt] = a[x];
        mb[cnt] = b[x];
      }
      for (int s = 0; s < (1 << cnt); ++s) {
        ll val = 0;
        for (int j = 0; j < cnt; ++j) if ((s >> j) & 1) val += ma[j + 1];
        for (int j = 1; j <= cnt; ++j)
          if ((s >> (j - 1)) & 1)
            for (int k = j + j; k <= cnt; k += j)
              if ((s >> (k - 1)) & 1)
                val -= mb[k];
        cur = max(cur, val);
      }
      ans += cur;
    }
  }
  cout << ans;

#ifdef LBT
  LOG("Time: %dms\n", int ((clock()
      -nol_cl) / (double)CLOCKS_PER_SEC * 1000));
#endif
  return 0;
}
  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对不起,由于我是一个文本交互的模型,我无法提供图像内容或直接链接到具体的题解或解决方案。但是,我可以帮你理解CSP-J2019公交换乘问题的基本概念和解决策略,这通常涉及到数据结构、图论以及算法设计。 CSP-J2019中的公交换乘问题可能是一个典型的旅行商问题(Traveling Salesman Problem, TSP)变种,或者是寻找最优路径的问题,其中涉及到公交网络中不同站点之间的最短路径或最少换乘次数。解决此类问题通常需要使用动态规划、贪心算法或者一些启发式搜索算法,比如A*搜索或Dijkstra算法。 如果你需要了解题目的基本思路,可能会这样操作: 1. 建立一个图,节点代表公交站点,边代表两个站点之间的路线及其长度或换乘次数。 2. 对于每个节点,计算从起点到所有其他节点的最短路径,形成一个邻接矩阵或邻接表。 3. 使用动态规划方法,例如记忆化搜索,尝试所有可能的路径,每次选择当前未访问节点中距离最近的一个,直到遍历完所有节点并回到起点,记录下总的距离或换乘次数。 4. 为了优化,可以考虑使用启发式搜索策略,如用估算的总距离作为启发信息,优先探索看起来更优的路径。 如果你对具体解法有疑问,或者想了解某个步骤的详细操作,请告诉我,我会尽力解释。至于详细的题解,建议你查阅相关的代码库、论坛帖子或在线教程,它们通常会有文字描述和步骤示例。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值