20200924 专题:李超线段树

总览:

维护若干一次函数的最值
李超线段树的结构和普通线段树一样的,只是它每个节点存的是该区间优势最大的线段(优势最大即暴露在最高折线中横坐标跨度最大)
李超线段树并不严格,只需满足包括单点的所有线段树区间“优势最大线段”中含有该单点的优势最大线段即可。
会出现如果一整个区间最高折线都被一条线段占了的话,只有最大的区间的“优势最大线段”是该线段的情况
插入时每个区间分类讨论
单点查询时遍历所有区间

T1 P4254 [JSOI2008]Blue Mary开公司

思路:板子题
代码:

#include <bits/stdc++.h>
using namespace std;

#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define pb push_back
#define mp make_pair

namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch()                                                                 \
  (p1_ == p2_ &&                                                             \
           (p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
       ? EOF                                                                 \
       : *p1_++)
inline int in() {
  int s = 0, f = 1;
  char x = ch();
  for (; x < '0' || x > '9'; x = ch())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = ch()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _p1 = -1;
inline void flush() { fwrite(_buf, 1, _p1 + 1, stdout), _p1 = -1; }
inline void pc(char x) {
  if (_p1 == (1 << 21) - 1) flush();
  _buf[++_p1] = x;
}
inline void out(int x) {
  char k[30];
  int pos = 0;
  if (!x) return pc('0');
  if (x < 0) pc('-'), x = -x;
  while (x) k[++pos] = (x % 10) | 48, x /= 10;
  for (int i = pos; i; i--) pc(k[i]);
}
}  // namespace IO
using namespace IO;

const int A = 1e5 + 5;
const int MAX = 5e4;
int n;
char opt[30];
struct node {
  double k, b;
} p[A];
int tot;

inline int New(double x, double y) {
  ++tot;
  p[tot].k = y, p[tot].b = x - y;
  return tot;
}

inline double calc(int id, int x) { return 1.0 * p[id].k * x + p[id].b; }
inline double inter(int x, int y) {
  return (p[y].b - p[x].b) / (p[x].k - p[y].k);
}

struct SGT {
  int l, r;
  int s;
} tr[4 * A];

#define ls x << 1
#define rs x << 1 | 1

inline void build(int x, int l, int r) {
  tr[x].l = l, tr[x].r = r;
  if (l == r) return;
  int mid = (l + r) >> 1;
  build(ls, l, mid), build(rs, mid + 1, r);
  return;
}

inline void insert(int x, int l, int r, int u) {
  int mid = (tr[x].l + tr[x].r) >> 1;
  if (tr[x].l >= l && tr[x].r <= r) {
    if (!tr[x].s) {
      tr[x].s = u;
      return;
    }
    int v = tr[x].s;
    double ly = calc(v, tr[x].l), ry = calc(v, tr[x].r), lyn = calc(u, tr[x].l),
           ryn = calc(u, tr[x].r);
    if (ly >= lyn && ry >= ryn) return;
    if (ly <= lyn && ry <= ryn) {
      tr[x].s = u;
      return;
    }
    double xx = inter(u, v);
    if (ly > lyn) {
      if (xx <= mid)
        tr[x].s = u, insert(ls, l, r, v);
      else
        insert(rs, l, r, u);

    } else {
      if (xx <= mid)
        insert(ls, l, r, u);
      else
        tr[x].s = u, insert(rs, l, r, v);
    }
    return;
  }
  if (l <= mid) insert(ls, l, r, u);
  if (r >= mid + 1) insert(rs, l, r, u);
  return;
}

inline int Max(int x, int y, int w) { return calc(x, w) > calc(y, w) ? x : y; }

inline int qurey(int x, int w) {
  if (tr[x].l == tr[x].r) return tr[x].s;
  int res = tr[x].s;
  int mid = (tr[x].l + tr[x].r) >> 1;
  if (w <= mid)
    res = Max(res, qurey(ls, w), w);
  else
    res = Max(res, qurey(rs, w), w);
  return res;
}

#undef ls
#undef rs

signed main() {
  scanf("%d", &n);
  build(1, 1, MAX);
  for (int i = 1; i <= n; i++) {
    scanf("%s", opt);
    if (opt[0] == 'P') {
      double u, v;
      scanf("%lf%lf", &u, &v);
      insert(1, 1, MAX, New(u, v));
    } else {
      int k;
      scanf("%d", &k);
      printf("%d\n", (int)(calc(qurey(1, k), k) / 100));
    }
  }
  flush();
  return 0;
}

T2 P4097 [HEOI2013]Segment

思路:板子题
代码:

#include <bits/stdc++.h>
using namespace std;

#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define pb push_back
#define mp make_pair

#define int long long

namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch()                                                                 \
  (p1_ == p2_ &&                                                             \
           (p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
       ? EOF                                                                 \
       : *p1_++)
inline int in() {
  int s = 0, f = 1;
  char x = ch();
  for (; x < '0' || x > '9'; x = ch())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = ch()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _p1 = -1;
inline void flush() { fwrite(_buf, 1, _p1 + 1, stdout), _p1 = -1; }
inline void pc(char x) {
  if (_p1 == (1 << 21) - 1) flush();
  _buf[++_p1] = x;
}
inline void out(int x) {
  char k[30];
  int pos = 0;
  if (!x) return pc('0');
  if (x < 0) pc('-'), x = -x;
  while (x) k[++pos] = (x % 10) | 48, x /= 10;
  for (int i = pos; i; i--) pc(k[i]);
}
}  // namespace IO
using namespace IO;

const int A = 4e5 + 5;
int n;
struct node {
  double k, b;
} p[A];
int tot;

inline void New(int x1, int y1, int x2, int y2) {
  int x = ++tot;
  if (x1 == x2)
    p[x].k = 0, p[x].b = max(y1, y2);
  else
    p[x].k = 1.0 * (y2 - y1) / (x2 - x1), p[x].b = 1.0 * y1 - 1.0 * x1 * p[x].k;
  return;
}

inline double calc(int id, int x) { return 1.0 * p[id].k * x + p[id].b; }
inline double inter(int x, int y) {
  return (p[x].b - p[y].b) / (p[y].k - p[x].k);
}
inline int check(int x, int y, int w) {
  return calc(x, w) > calc(y, w) ? x : y;
}

struct SGT {
  int l, r;
  int s;
} tr[4 * A];

#define ls x << 1
#define rs x << 1 | 1

inline void build(int x, int l, int r) {
  tr[x].l = l, tr[x].r = r;
  if (l == r) return;
  int mid = (l + r) >> 1;
  build(ls, l, mid), build(rs, mid + 1, r);
  return;
}

inline void insert(int x, int l, int r, int u) {
  int mid = (tr[x].l + tr[x].r) >> 1;
  if (tr[x].l >= l && tr[x].r <= r) {
    if (!tr[x].s) {
      tr[x].s = u;
      return;
    }
    int v = tr[x].s;
    double ly = calc(v, tr[x].l), ry = calc(v, tr[x].r), lyn = calc(u, tr[x].l),
           ryn = calc(u, tr[x].r);
    if (ly >= lyn && ry >= ryn) return;
    if (lyn >= ly && ryn >= ry) {
      tr[x].s = u;
      return;
    }
    double xx = inter(u, v);
    if (ly > lyn) {
      if (xx <= mid)
        insert(ls, l, r, v), tr[x].s = u;
      else
        insert(rs, l, r, u);
    } else {
      if (xx <= mid)
        insert(ls, l, r, u);
      else
        insert(rs, l, r, v), tr[x].s = u;
    }
  }
  if (l <= mid) insert(ls, l, r, u);
  if (r >= mid + 1) insert(rs, l, r, u);
  return;
}

inline int find(int x, int w) {
  if (tr[x].l == tr[x].r) return tr[x].s;
  int res = tr[x].s;
  int mid = (tr[x].l + tr[x].r) >> 1;
  if (w <= mid)
    res = check(res, find(ls, w), w);
  else
    res = check(res, find(rs, w), w);
  return res;
}

#undef ls
#undef rs

signed main() {
  n = in();
  build(1, 1, 39989);
  int ans = 0;
  for (int i = 1; i <= n; i++) {
    int opt = in();
    if (!opt) {
      int x = (in() + ans - 1) % 39989 + 1;
      out(ans = find(1, x)), pc('\n');
    } else {
      int x1 = (in() + ans - 1) % 39989 + 1,
          y1 = (in() + ans - 1) % 1000000000 + 1,
          x2 = (in() + ans - 1) % 39989 + 1,
          y2 = (in() + ans - 1) % 1000000000 + 1;
      if (x1 > x2) swap(x1, x2), swap(y1, y2);
      New(x1, y1, x2, y2);
      insert(1, x1, x2, tot);
    }
  }
  flush();
  return 0;
}

T3 P4069 [SDOI2016]游戏

思路:
树剖+李超线段树
对于 A ∗ d i s + B A*dis+B Adis+B,将它分成 s − > l c a , l c a − > t s->lca,lca->t s>lca,lca>t
d [ i ] d[i] d[i] i i i 到根的距离
s − > l c a : A ∗ ( d [ s ] − d [ x ] ) + B = − A ∗ d [ x ] + A ∗ d [ s ] + B l c a − > t : A ∗ ( d [ s ] + d [ x ] − 2 ∗ d [ l c a ] ) + B = A ∗ d [ x ] + A ∗ d [ s ] − 2 ∗ A ∗ d [ l c a ] + B s->lca:\\ A*(d[s]-d[x])+B=-A*d[x]+A*d[s]+B\\ lca->t:\\ A*(d[s]+d[x]-2*d[lca])+B=A*d[x]+A*d[s]-2*A*d[lca]+B s>lca:A(d[s]d[x])+B=Ad[x]+Ad[s]+Blca>t:A(d[s]+d[x]2d[lca])+B=Ad[x]+Ad[s]2Ad[lca]+B
注意每个下标的点值是到根的距离
维护区间最小就每次用优势最大线段的两头更新答案

代码:

#include <bits/stdc++.h>
using namespace std;

#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define pb push_back
#define mp make_pair

#define int long long

namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch()                                                                 \
  (p1_ == p2_ &&                                                             \
           (p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
       ? EOF                                                                 \
       : *p1_++)
inline int in() {
  int s = 0, f = 1;
  char x = ch();
  for (; x < '0' || x > '9'; x = ch())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = ch()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _p1 = -1;
inline void flush() { fwrite(_buf, 1, _p1 + 1, stdout), _p1 = -1; }
inline void pc(char x) {
  if (_p1 == (1 << 21) - 1) flush();
  _buf[++_p1] = x;
}
inline void out(int x) {
  char k[30];
  int pos = 0;
  if (!x) return pc('0');
  if (x < 0) pc('-'), x = -x;
  while (x) k[++pos] = (x % 10) | 48, x /= 10;
  for (int i = pos; i; i--) pc(k[i]);
}
}  // namespace IO
using namespace IO;

const int A = 4e5 + 5;
const int INF = 123456789123456789;
int n, m;
int head[A], tot_road;
struct Road {
  int nex, to, w;
} road[2 * A];
inline void edge(int x, int y, int w) {
  road[++tot_road] = {head[x], y, w}, head[x] = tot_road;
}
int f[A], dis[A], dep[A], sz[A], son[A], top[A], pos[A], idx[A], tot_cut;

inline void DFS1(int fa, int x) {
  f[x] = fa, dep[x] = dep[fa] + 1, sz[x] = 1;
  for (int y = head[x]; y; y = road[y].nex) {
    int z = road[y].to, w = road[y].w;
    if (z == fa) continue;
    dis[z] = dis[x] + w;
    DFS1(x, z);
    sz[x] += sz[z];
    if (sz[z] > sz[son[x]]) son[x] = z;
  }
  return;
}

inline void DFS2(int x) {
  pos[x] = ++tot_cut, idx[pos[x]] = x;
  if (son[x]) {
    top[son[x]] = top[x];
    DFS2(son[x]);
  }
  for (int y = head[x]; y; y = road[y].nex) {
    int z = road[y].to;
    if (top[z]) continue;
    top[z] = z;
    DFS2(z);
  }
  return;
}

inline int lca(int x, int y) {
  while (top[x] != top[y]) {
    if (dep[top[x]] < dep[top[y]]) swap(x, y);
    x = f[top[x]];
  }
  return dep[x] < dep[y] ? x : y;
}

inline void tree_cut() {
  DFS1(0, 1);
  top[1] = 1;
  DFS2(1);
  return;
}

struct node {
  int k, b;
} p[A];
int tot_node;

inline int New(int k, int b) {
  int x = ++tot_node;
  p[x].k = k, p[x].b = b;
  return x;
}

inline int calc(int id, int x) { return p[id].k * dis[idx[x]] + p[id].b; }

struct SGT {
  int l, r;
  int s, mn;
} tr[4 * A];

#define ls x << 1
#define rs x << 1 | 1

inline void build(int x, int l, int r) {
  tr[x].l = l, tr[x].r = r;
  tr[x].s = 1, tr[x].mn = INF;
  if (l == r) return;
  int mid = (l + r) >> 1;
  build(ls, l, mid), build(rs, mid + 1, r);
  return;
}

inline void pushup(int x) {
  tr[x].mn = min(tr[x].mn, min(tr[ls].mn, tr[rs].mn));
}

inline void insert(int x, int l, int r, int u) {
  int mid = (tr[x].l + tr[x].r) >> 1;
  if (tr[x].l >= l && tr[x].r <= r) {
    if (!tr[x].s) {
      tr[x].s = u;
      tr[x].mn = min(tr[x].mn, min(calc(u, tr[x].l), calc(u, tr[x].r)));
      return;
    }
    int v = tr[x].s;
    int ly = calc(v, tr[x].l), ry = calc(v, tr[x].r), lyn = calc(u, tr[x].l),
        ryn = calc(u, tr[x].r);
    if (ly <= lyn && ry <= ryn) return;
    if (ly >= lyn && ry >= ryn) {
      tr[x].s = u;
      tr[x].mn = min(tr[x].mn, min(calc(u, tr[x].l), calc(u, tr[x].r)));
      return;
    }
    int my = calc(v, mid), myn = calc(u, mid);
    if (ly > lyn) {
      if (my > myn)
        insert(rs, l, r, v), tr[x].s = u;
      else
        insert(ls, l, r, u);
    } else {
      if (my > myn)
        insert(ls, l, r, v), tr[x].s = u;
      else
        insert(rs, l, r, u);
    }
    tr[x].mn = min(tr[x].mn, min(calc(u, tr[x].l), calc(u, tr[x].r)));
    pushup(x);
    return;
  }
  if (l <= mid) insert(ls, l, r, u);
  if (r >= mid + 1) insert(rs, l, r, u);
  pushup(x);
  return;
}

inline int find(int x, int l, int r) {
  if (tr[x].l >= l && tr[x].r <= r) return tr[x].mn;
  int ans = INF;
  int mid = (tr[x].l + tr[x].r) >> 1;
  if (p[tr[x].s].b != INF)
    ans = min(ans, min(calc(tr[x].s, max(tr[x].l, l)),
                       calc(tr[x].s, min(tr[x].r, r))));
  if (l <= mid) ans = min(ans, find(ls, l, r));
  if (r >= mid + 1) ans = min(ans, find(rs, l, r));
  return ans;
}

#undef ls
#undef rs

inline void road_add(int x, int y) {
  while (top[x] != top[y]) {
    if (dep[top[x]] < dep[top[y]]) swap(x, y);
    insert(1, pos[top[x]], pos[x], tot_node);
    x = f[top[x]];
  }
  if (dep[x] > dep[y]) swap(x, y);
  insert(1, pos[x], pos[y], tot_node);
  return;
}

inline int road_qurey(int x, int y) {
  int ans = INF;
  while (top[x] != top[y]) {
    if (dep[top[x]] < dep[top[y]]) swap(x, y);
    ans = min(ans, find(1, pos[top[x]], pos[x]));
    x = f[top[x]];
  }
  if (dep[x] > dep[y]) swap(x, y);
  ans = min(ans, find(1, pos[x], pos[y]));
  return ans;
}

signed main() {
  n = in(), m = in();
  for (int i = 1; i < n; i++) {
    int u = in(), v = in(), w = in();
    edge(u, v, w), edge(v, u, w);
  }
  tree_cut();
  New(0, INF);
  build(1, 1, n);
  while (m--) {
    int opt = in();
    if (opt == 1) {
      int s = in(), t = in(), a = in(), b = in();
      New(-a, a * dis[s] + b);
      road_add(s, lca(s, t));
      New(a, a * (dis[s] - 2 * dis[lca(s, t)]) + b);
      road_add(lca(s, t), t);
    } else {
      int s = in(), t = in();
      out(road_qurey(s, t)), pc('\n');
    }
  }
  flush();
  return 0;
}

T4 P4655 [CEOI2017]Building Bridges

思路:
李超线段树还可以维护斜率优化
虽然复杂度多个log
f i = a i × b j + c i + d j f_i=a_i\times b_j+c_i+d_j fi=ai×bj+ci+dj
转化成
f i + c i = b j × a i + d j f_i+c_i=b_j\times a_i+d_j fi+ci=bj×ai+dj
后面就是一个一次函数在 a i a_i ai 处的最值
这道题的式子
s n = ∑ w i s_n=\sum w_i sn=wi
f i = min ⁡ { f j + h i 2 + h j 2 − 2 h i h j + s i − 1 − s j } f i = h i 2 + s i − 1 + min ⁡ { − 2 h j h i + f j + h j 2 − s j } f_i=\min \{f_j+h_i^2+h_j^2-2h_ih_j+s_{i-1}-s_j\}\\ \\ f_i=h_i^2+s_{i-1}+\min \{-2h_jh_i+f_j+h_j^2-s_j\} fi=min{fj+hi2+hj22hihj+si1sj}fi=hi2+si1+min{2hjhi+fj+hj2sj}
转移就完了
以后再也不用写动态凸包/cdq分治辣

代码:

#include <bits/stdc++.h>
using namespace std;

#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define pb push_back
#define mp make_pair

#define int long long

namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch()                                                                 \
  (p1_ == p2_ &&                                                             \
           (p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
       ? EOF                                                                 \
       : *p1_++)
inline int in() {
  int s = 0, f = 1;
  char x = ch();
  for (; x < '0' || x > '9'; x = ch())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = ch()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _p1 = -1;
inline void flush() { fwrite(_buf, 1, _p1 + 1, stdout), _p1 = -1; }
inline void pc(char x) {
  if (_p1 == (1 << 21) - 1) flush();
  _buf[++_p1] = x;
}
inline void out(int x) {
  char k[30];
  int pos = 0;
  if (!x) return pc('0');
  if (x < 0) pc('-'), x = -x;
  while (x) k[++pos] = (x % 10) | 48, x /= 10;
  for (int i = pos; i; i--) pc(k[i]);
}
}  // namespace IO
using namespace IO;

const int A = 1e5 + 5;
const int INF = 1e12;
int n;
int h[A], hh[A], w[A], s[A], len;
int f[A];

inline void lsh() {
  sort(hh + 1, hh + 1 + n);
  len = unique(hh + 1, hh + 1 + n) - (hh + 1);
  for (int i = 1; i <= n; i++)
    h[i] = lower_bound(hh + 1, hh + 1 + len, h[i]) - hh;
  return;
}

struct node {
  int k, b;
} p[A];
int tot_node;

inline void New(int k, int b) {
  int x = ++tot_node;
  p[x].k = k, p[x].b = b;
  return;
}

inline int calc(int id, int w) { return p[id].k * hh[w] + p[id].b; }

struct SGT {
  int l, r;
  int s;
} tr[4 * A];

#define ls x << 1
#define rs x << 1 | 1

inline void build(int x, int l, int r) {
  tr[x].l = l, tr[x].r = r;
  if (l == r) return;
  int mid = (l + r) >> 1;
  build(ls, l, mid), build(rs, mid + 1, r);
  return;
}

inline void insert(int x, int l, int r, int u) {
  int mid = (tr[x].l + tr[x].r) >> 1;
  if (tr[x].l >= l && tr[x].r <= r) {
    if (!tr[x].s) {
      tr[x].s = u;
      return;
    }
    int v = tr[x].s;
    int ly = calc(v, tr[x].l), ry = calc(v, tr[x].r);
    int lyn = calc(u, tr[x].l), ryn = calc(u, tr[x].r);
    if (ly <= lyn && ry <= ryn) return;
    if (ly >= lyn && ry >= ryn) {
      tr[x].s = u;
      return;
    }
    int my = calc(v, mid), myn = calc(u, mid);
    if (ly <= lyn) {
      if (my >= myn)
        insert(ls, l, r, v), tr[x].s = u;
      else
        insert(rs, l, r, u);
    } else {
      if (my >= myn)
        insert(rs, l, r, v), tr[x].s = u;
      else
        insert(ls, l, r, u);
    }
    return;
  }
  if (l <= mid) insert(ls, l, r, u);
  if (r >= mid + 1) insert(rs, l, r, u);
  return;
}

inline int Max(int x, int y, int w) { return calc(x, w) <= calc(y, w) ? x : y; }

inline int find(int x, int w) {
  if (tr[x].l == tr[x].r) return tr[x].s;
  int res = tr[x].s;
  int mid = (tr[x].l + tr[x].r) >> 1;
  if (w <= mid)
    res = Max(res, find(ls, w), w);
  else
    res = Max(res, find(rs, w), w);
  return res;
}

#undef ls
#undef rs

signed main() {
  n = in();
  for (int i = 1; i <= n; i++) h[i] = hh[i] = in();
  for (int i = 1; i <= n; i++) w[i] = in(), s[i] = s[i - 1] + w[i];
  lsh();
  build(1, 1, len);
  p[0].k = p[0].b = INF;
  New(-2 * hh[h[1]], f[1] + hh[h[1]] * hh[h[1]] - s[1]);
  insert(1, 1, len, tot_node);
  for (int i = 2; i <= n; i++) {
    int val = calc(find(1, h[i]), h[i]);
    f[i] = hh[h[i]] * hh[h[i]] + s[i - 1] + val;
    New(-2 * hh[h[i]], f[i] + hh[h[i]] * hh[h[i]] - s[i]);
    insert(1, 1, n, tot_node);
  }
  out(f[n]), pc('\n');
  flush();
  return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值