数据结构训练

数据结构训练

可并堆

学习了一个斜堆的写法,非常好写,于是又去切了可并堆模板[Apio2012]dispatching:

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

const int MAXN = 100005;

struct lheap {
    long long dat;
    int lc, rc;
    lheap() { lc = rc = 0; }
} tree[MAXN*2];
int tp = 0;

int merge(int a, int b)
{
    if (a == 0 || b == 0) return a+b;
    if (tree[a].dat < tree[b].dat) swap(a, b);
    swap(tree[a].lc, tree[a].rc);
    return tree[a].rc = merge(tree[a].rc, b), a;
}

long long pop(int &a)
{
    long long ret = tree[a].dat;
    a = merge(tree[a].lc, tree[a].rc);
    return ret;
}

struct node {
    int to, next;
} edge[MAXN];
int head[MAXN], top = 0;
inline void push(int i, int j)
{ edge[++top] = (node){j, head[i]}, head[i] = top; }

int n;
long long m, ans = 0;
int b[MAXN];
long long c[MAXN], l[MAXN];
int rt;

int dfs(int nd, long long &sig, int &cnt)
{
    int tr = ++tp;
    long long sum; int ct;
    sig = tree[tr].dat = c[nd], cnt = 1;
    for (int i = head[nd]; i; i = edge[i].next)
        tr = merge(tr, dfs(edge[i].to, sum, ct)), sig += sum, cnt += ct;
    while (sig > m) sig -= pop(tr), cnt--;
    ans = max(ans, cnt*l[nd]);
    return tr;
}

int main()
{
    scanf("%d%lld", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%d%lld%lld", &b[i], &c[i], &l[i]);
        if (b[i]) push(b[i], i);
        else rt = i;
    }
    long long t;
    int cnt;
    dfs(rt, t, cnt);
    printf("%lld\n", ans);
    return 0;
}

bzoj1078: [SCOI2008]斜堆

结论题/找规律题,递归求解发现应该交替插入,然后只要分类讨论四种情况就好。

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

const int MAXN = 105;

int lc[MAXN], rc[MAXN], n;

int solve(int nd, int a[], int &top)
{
    top = 0;
    int l[MAXN], r[MAXN], ltop = 0, rtop = 0, lf = 0, rf = 0;
    if (lc[nd] != -1) solve(lc[nd], l, ltop);
    if (rc[nd] != -1) solve(rc[nd], r, rtop);
    if (ltop-rtop > 1) {
        while (ltop-lf > rtop-rf) a[++top] = l[++lf];
        a[++top] = nd;
        while (ltop-lf > 0) a[++top] = r[++rf], a[++top] = l[++lf];
    } else if (ltop == rtop+1) {
        a[++top] = nd;
        while (ltop-lf > 0) {
            a[++top] = l[++lf];
            if (rtop-rf > 0) a[++top] = r[++rf];
        }
    } else if (ltop == rtop) {
        a[++top] = nd;
        while (rtop-rf > 0)
            a[++top] = r[++rf], a[++top] = l[++lf];
    } else {
        while (rtop-rf > ltop-lf-1) a[++top] = r[++rf];
        a[++top] = nd;
        while (ltop-lf > 0) {
            a[++top] = l[++lf];
            if (rtop-rf > 0) a[++top] = r[++rf];
        }
    }
}

int main()
{
    int a[MAXN], u;
    memset(lc, -1, sizeof lc);
    memset(rc, -1, sizeof rc);
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &u);
        if (u < 100) lc[u] = i;
        else rc[u-100] = i;
    }
    solve(0, a, u);
    for (int i = 1; i <= u; i++)
        printf("%d ", a[i]);
    return 0;
}

bzoj4003: [JLOI2015]城池攻占

比较有意思的数据结构题。大概就是用可并堆维护还存活着的骑士,然后只需要lazy_tag就好了。

注意两种lazy_tag一定要考虑彼此的影响!

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

const int MAXN = 300005;

struct sheap {
    pair<long long, int> dat;
    int lc, rc;
    long long tag1, tag2;
    sheap() { tag1 = 0, tag2 = 1; }
} tree[MAXN*2];

int tp = 0;

long long a[MAXN], b[MAXN];
int n, m;
long long h[MAXN];
long long s[MAXN];
int c[MAXN], u;
vector<int> v[MAXN];

inline void pdw(int nd)
{
    if (!nd) return; 
    tree[nd].dat.first *= tree[nd].tag2;
    if (tree[nd].lc)
        tree[tree[nd].lc].tag2 *= tree[nd].tag2, tree[tree[nd].lc].tag1 *= tree[nd].tag2;
    if (tree[nd].rc)
        tree[tree[nd].rc].tag2 *= tree[nd].tag2, tree[tree[nd].rc].tag1 *= tree[nd].tag2;
    tree[nd].dat.first += tree[nd].tag1;
    if (tree[nd].lc) tree[tree[nd].lc].tag1 += tree[nd].tag1;
    if (tree[nd].rc) tree[tree[nd].rc].tag1 += tree[nd].tag1;
    tree[nd].tag1 = 0, tree[nd].tag2 = 1;
}

inline void mul_dt(int nd, long long dt)
{ if (!nd) return; tree[nd].tag2 *= dt, tree[nd].tag1 *= dt; }
inline void plus_dt(int nd, long long dt)
{ if (!nd) return; tree[nd].tag1 += dt; }

void tour(int nd, int tab = 0)
{
    if (!nd) return;
    pdw(nd);
    tour(tree[nd].lc, tab+2), tour(tree[nd].rc, tab+2);
}
int merge(int a, int b)
{
    if (a == b && a != 0) throw;
    pdw(a), pdw(b);
    if (a == 0 || b == 0) return a+b;
    if (tree[a].dat.first > tree[b].dat.first) swap(a, b);
    swap(tree[a].lc, tree[a].rc);
    return tree[a].lc = merge(tree[a].lc, b), a;
}

inline long long top_ele(int a)
{ return pdw(a), tree[a].dat.first; }

inline void pop(int &a)
{
    pdw(a);
    a = merge(tree[a].lc, tree[a].rc);
}

struct node {
    int to, next;
} edge[MAXN];
int head[MAXN], top = 0;
inline void push(int i, int j)
{ edge[++top] = (node){j, head[i]}, head[i] = top; }

int cnt[MAXN], depth[MAXN], death[MAXN];

int dfs(int nd)
{
    int hp = 0;
    for (int i = 0; i < v[nd].size(); i++) {
        hp = merge(hp, (tree[++tp].dat = make_pair(s[v[nd][i]], v[nd][i]), tp));
    }
    for (int i = head[nd]; i; i = edge[i].next) {
        depth[edge[i].to] = depth[nd]+1;
        hp = merge(hp, dfs(edge[i].to));
    }
    while (hp && top_ele(hp) < h[nd]) {
        pair<long long, int> dat = tree[hp].dat;
        death[dat.second] = nd, cnt[nd]++;
        pop(hp);
    }
    if (!hp) return hp;
    if (a[nd] == 0) plus_dt(hp, b[nd]);
    else mul_dt(hp, b[nd]);
    return hp;
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%lld", &h[i]);
    for (int i = 2; i <= n; i++) 
        scanf("%d%lld%lld", &u, &a[i], &b[i]), push(u, i);
    for (int i = 1; i <= m; i++){
        scanf("%lld%d", &s[i], &c[i]);
        v[c[i]].push_back(i);
    }
    depth[1] = 1;
    dfs(1);
    for (int i = 1; i <= n; i++) printf("%d\n", cnt[i]);
    for (int i = 1; i <= m; i++) printf("%d\n", depth[c[i]]-depth[death[i]]);
    return 0;
}

树状数组

bzoj4415: [Shoi2013]发牌

类似约瑟夫问题,树状数组上二分小trick,貌似一年前就在千古神犇lcy的课上学习过。就是二进制一位一位确定。

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

const int MAXN = 700005;

inline int lowbit(int i) { return i&(-i); }
int c[MAXN], n;
void modify(int pos, int dt)
{ for (int i = pos; i <= n; i += lowbit(i)) c[i] += dt; }

int sum(int k)
{
    int now = 0, dat = 0;
    for (int i = 1<<25; i; i >>= 1) {
        if (now+i <= n && dat+c[i+now] < k)
            dat += c[i+now], now += i;
    }
    return now+1;
}

int query(int a)
{
    int ans = 0;
    for (int i = a; i; i -= lowbit(i))
        ans += c[i];
    return ans;
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) modify(i, 1);
    int now = 0;
    for (int i = 1; i <= n; i++) {
        int last = n-i+1, k;
        scanf("%d", &k);
        k += query(now) + 1;
        int p = k%last;
        if (p == 0) p = last;
        now = sum(p), modify(now, -1);
        printf("%d\n", now);
    }
    return 0;
}

bzoj4418: [Shoi2013]扇形面积并

出题人大概没有学过高中数学…明明是均分 [0,π] 之间的区间。

一开始还以为需要玄学可持久化,后来发现只需要在合适的地方打个表,然后一块一块扫过去,用树状数组kth就好。由于是寻找最大,可以通过 pMAXRp+1 变成最小。

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

const int MAXN = 300005;

int c[MAXN*10], n, m, k, r = 2000001;

inline int lowbit(int i) { return i&(-i); }
void modify(int nd, int dt)
{ for (int i = nd; i <= r; i += lowbit(i)) c[i] += dt; }
int sum(int nd)
{ int ans = 0; for (int i = nd; i; i -= lowbit(i)) ans += c[i]; return ans; }
int query(int l, int r)
{ return sum(r)-sum(l-1); }
int kth(int nd)
{
    int now = 0, dat = 0;
    for (int i = 1<<27; i; i >>= 1) {
        if (now+i <= r && dat+c[i+now] < nd)
            dat += c[i+now], now += i;
    }
    return now+1;
}

vector<int> add[MAXN*10];

void solve()
{
    long long ans = 0;
    for (int i = 1; i <= m; i++) {
        for (int j = 0; j < add[i].size(); j++) {
            if (add[i][j] > 0) modify(r-add[i][j]+1, 1);
            else modify(r-(-add[i][j])+1, -1);
        }
        long long rm = r-kth(k)+1;
        if (rm <= r) ans += rm*rm;
    }
    printf("%lld\n", ans);
}

int main()
{
    scanf("%d%d%d", &n, &m, &k);
    m *= 2;
    for (int i = 1; i <= n; i++) {
        int r, a, b;
        scanf("%d%d%d", &r, &a, &b);
        if (a < 0) a += m;
        if (b < 0) b += m;  
        if (a < b) add[a+1].push_back(r), add[b+1].push_back(-r);
        else if (a > b) 
            add[1].push_back(r), add[b+1].push_back(-r), add[a+1].push_back(r);
        else add[1].push_back(r);
    }
    solve();
    return 0;
}

bzoj1455: 罗马游戏

居然可以把死人和活人变成一组……昏君啊…

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

const int MAXN = 1000005;
struct sheap {
    int lc, rc, dat;
    sheap() { lc = rc = 0; }
} tree[MAXN];

int merge(int a, int b)
{
    if (a == 0 || b == 0) return a+b;
    if (tree[a].dat > tree[b].dat) swap(a, b);
    swap(tree[a].lc, tree[a].rc);
    return tree[a].lc = merge(tree[a].lc, b), a;
}

set<int> st;

int pop(int &a)
{
    int ret = tree[a].dat;
    st.insert(ret);
    a = merge(tree[a].lc, tree[a].rc);
    return ret;
}

int fa[MAXN];
inline int findf(int nd)
{ return fa[nd]?fa[nd]=findf(fa[nd]):nd; }
int n, m;
char str[10];
int htp[MAXN], dat[MAXN];

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", &tree[i].dat), dat[i] = tree[i].dat, htp[i] = i;
    scanf("%d", &m);
    for (int i = 1; i <= m; i++) {
        scanf("%s", str);
        if (str[0] == 'M') {
            int x, y;
            scanf("%d%d", &x, &y);
            if (st.count(dat[x]) || st.count(dat[y])) continue;
            x = findf(x), y = findf(y);
            if (x != y) fa[x] = y, htp[y] = merge(htp[x], htp[y]);
        } else {
            int x;
            scanf("%d", &x);
            if (st.count(dat[x])) puts("0");
            else
                printf("%d\n", pop(htp[findf(x)]));
        }
    }
    return 0;
}

bzoj2006: [NOI2010]超级钢琴

堆+ST表。
堆维护 (i,l,r) 表示从 i 开始,终点在[l,r]之间的区间最大值,每次取出最大值后将区间分裂。因此需要用ST表维护区间最大值和最大值位置。

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

const int MAXN = 500005;

long long f[MAXN][22];
int pos[MAXN][22];
long long a[MAXN];

int n, k, l, r;

inline long long max_val(int l, int r)
{
    int k = int(log2(r-l+1)+0.0000001);
    return max(f[l][k], f[r-(1<<k)+1][k]);
}

inline int max_pos(int l, int r)
{
    int k = int(log2(r-l+1)+0.0000001);
    if (f[l][k] >= f[r-(1<<k)+1][k]) return pos[l][k];
    return pos[r-(1<<k)+1][k];
}

struct query {
    int i, l, r;
    inline long long val()
    {
        return max_val(l, r)-a[i-1];
    }
    inline int pos()
    {
        return max_pos(l, r);
    }
    inline friend bool operator < (query a, query b)
    {
        return a.val() < b.val();
    }
};

priority_queue<query> hp;

int main()
{
    scanf("%d%d%d%d", &n, &k, &l, &r);
    for (register int i = 1; i <= n; i++)
        scanf("%lld", &a[i]), a[i] += a[i-1];
    for (register int i = 1; i <= n; i++) f[i][0] = a[i], pos[i][0] = i;
    for (register int j = 1; j <= 21; j++)
        for (register int i = 1; i <= n; i++) {
            f[i][j] = f[i][j-1], pos[i][j] = pos[i][j-1];
            if (i+(1<<(j-1)) <= n && f[i+(1<<(j-1))][j-1] > f[i][j]) {
                f[i][j] = f[i+(1<<(j-1))][j-1];
                pos[i][j] = pos[i+(1<<(j-1))][j-1];
            }
        }
    for (register int i = 1; i <= n; i++)
        if (i+l-1 <= n)
            hp.push((query){i, i+l-1, min(n, i+r-1)});
    long long ans = 0;
    for (register int i = 1; i <= k; i++) {
        query tp = hp.top(); hp.pop();
        ans += tp.val();
        int p = tp.pos();
        if (p-1 >= tp.l) hp.push((query){tp.i, tp.l, p-1});
        if (p+1 <= tp.r) hp.push((query){tp.i, p+1, tp.r});
    }
    cout << ans << endl;
    return 0;
}

bzoj3211: 花神游历各国

非负!=正..
如果早做了这题大概就会做出省选相逢是问候了吧…

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

const int MAXN = 100005;

int fa[MAXN];
inline int findf(int nd)
{ return fa[nd]!=nd?fa[nd] = findf(fa[nd]):nd; }
inline int lowbit(int i) { return i&(-i); }
long long c[MAXN], dat[MAXN];
int n, m;
void modify(int nd, int dt)
{ for (int i = nd; i <= n; i += lowbit(i)) c[i] += dt; }
long long sum(int nd)
{ long long ans = 0; for (int i = nd; i; i -= lowbit(i)) ans += c[i]; return ans; }
long long query(int l, int r)
{ return sum(r)-sum(l-1); }

inline int read()
{
    int a = 0, c;
    do c = getchar(); while (!isdigit(c));
    while (isdigit(c)) {
        a = a*10+c-'0';
        c = getchar();
    }
    return a;
}

int main()
{
    scanf("%d", &n);
    for (register int i = 1; i <= n; i++) c[i] = read(), dat[i] = c[i], fa[i] = i;
    for (register int i = 1; i <= n; i++) if (i+lowbit(i)<=n) c[i+lowbit(i)] += c[i];
    fa[n+1] = n+1;
    scanf("%d", &m);
    for (register int i = 1; i <= m; i++) {
        int opt, l, r, d;
        opt = read(), l = read(), r = read();
        if (opt == 1) printf("%lld\n", query(l, r));
        else {
            while (l <= r) {
                d = dat[l]; 
                d = (int)(sqrt(d))-d;
                modify(l, d), dat[l] += d;
                if (dat[l] <= 1) fa[l] = l+1;
                l = findf(l+1);
            }
        }
    }
    return 0;
}

线段树

bzoj4592: [Shoi2015]脑洞治疗仪

比较关键的是 nlgn 完成治疗。

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

const int MAXN = 200005;

int dat[MAXN*4], lc[MAXN*4], rc[MAXN*4], l[MAXN*4], r[MAXN*4];
int change[MAXN*4], llen[MAXN*4], rlen[MAXN*4], len[MAXN*4], top = 0, root = 0;

inline int new_node(int opl, int opr)
{ return ++top, l[top] = opl, r[top] = opr, top; }

inline void pdw(int nd) // 1 nh / -1 hole
{
    if (!change[nd]) return;
    change[lc[nd]] = change[rc[nd]] = change[nd]*(lc[nd]!=0);
    dat[nd] = (r[nd]-l[nd]+1)*(change[nd]==1);
    llen[nd] = rlen[nd] = len[nd] = (r[nd]-l[nd]+1)*(change[nd]==-1);
    change[nd] = 0;
}

inline void update(int nd)
{
    pdw(nd), pdw(lc[nd]), pdw(rc[nd]);
    if (l[nd] == r[nd]) {
        llen[nd] = rlen[nd] = len[nd] = (dat[nd]==0);
        return;
    }
    llen[nd] = llen[lc[nd]]+(dat[lc[nd]] == 0)*llen[rc[nd]];
    rlen[nd] = rlen[rc[nd]]+(dat[rc[nd]] == 0)*rlen[lc[nd]];
    len[nd] = max(max(len[lc[nd]], len[rc[nd]]), rlen[lc[nd]]+llen[rc[nd]]);
    dat[nd] = dat[lc[nd]]+dat[rc[nd]];
}

int pat[MAXN];

void build(int &nd, int l, int r)
{
    nd = new_node(l, r);
    if (l == r) dat[nd] = pat[r];
    else build(lc[nd], l, (l+r)/2), build(rc[nd], (l+r)/2+1, r);
    update(nd);
}

void modify(int nd, int opl, int opr, int alc)
{
    if (opl > opr) return;
    pdw(nd);
    if (opl == l[nd] && opr == r[nd]) change[nd] = alc, pdw(nd);
    else {
        int mid = (l[nd]+r[nd])/2;
        if (opr <= mid) modify(lc[nd], opl, opr, alc);
        else if (opl > mid) modify(rc[nd], opl, opr, alc);
        else modify(lc[nd], opl, mid, alc), modify(rc[nd], mid+1, opr, alc);
        pdw(lc[nd]), pdw(rc[nd]), update(nd);
    }
}

int query(int nd, int opl, int opr)
{
    pdw(nd);
    if (opl == l[nd] && opr == r[nd]) return len[nd];
    else {
        int mid = (l[nd]+r[nd])/2;
        pdw(lc[nd]), pdw(rc[nd]);
        if (opr <= mid) return query(lc[nd], opl, opr);
        else if (opl > mid) return query(rc[nd], opl, opr);
        else return max(max(query(lc[nd], opl, mid), query(rc[nd], mid+1, opr)), 
                min(rlen[lc[nd]], mid-opl+1)+min(llen[rc[nd]], opr-mid));
    }
}

void dfs(int nd, int tab = 0)
{
    if (!nd) return;
    for (int i = 1; i <= tab; i++) putchar(' ');
    printf("[%d, %d], dat = %d, llen = %d, rlen = %d, len = %d, change = %d\n", l[nd], r[nd], dat[nd], llen[nd], rlen[nd], len[nd], change[nd]);
    dfs(lc[nd], tab+2), dfs(rc[nd], tab+2);
}

int query_sum(int nd, int opl, int opr)
{
    if (opl > opr) return 0;
    pdw(nd);
    int mid = (l[nd]+r[nd])/2;
    if (opl == l[nd] && opr == r[nd]) return dat[nd];
    else return query_sum(lc[nd], opl, min(opr, mid))+query_sum(rc[nd], max(opl, mid+1), opr);
}

int last_pos(int nd, int k) // in node nd, the first place to have k holes
{
    if (l[nd] == r[nd]) return l[nd];
    pdw(nd), pdw(lc[nd]), pdw(rc[nd]);
    int lp = r[lc[nd]]-l[lc[nd]]+1-dat[lc[nd]];
    if (lp >= k) return last_pos(lc[nd], k);
    else return last_pos(rc[nd], k-lp);
}

void fix(int ll, int lr, int nl, int nr)
{
    int dt = query_sum(root, ll, lr);
    modify(root, ll, lr, -1);
    if (dt == 0) return;
    int k = last_pos(root, nl-1-query_sum(root, 1, nl-1)+dt);
    modify(root, nl, min(nr, k), 1);
}

inline int read()
{
    int a = 0, c;
    do c = getchar(); while (!isdigit(c));
    while (isdigit(c))
        a = a*10+c-'0', c = getchar();
    return a;
}

int n, m;

int main()
{
    n = read(), m = read();
    for (int i = 1; i <= n; i++) pat[i] = 1;
    build(root, 1, n);
    for (int i = 1; i <= m; i++) {
        int tp, l, r, u, v;
        tp = read();
        if (tp == 0) l = read(), r = read(), modify(root, l, r, -1);
        else if (tp == 1) l = read(), r = read(), u = read(), v = read(), fix(l, r, u, v);
        else l = read(), r = read(), printf("%d\n", query(root, l, r));
    }
    return 0;
}

bzoj2212: [Poi2011]Tree Rotations

线段树合并小技巧。
貌似空间是 O(nlgn) 的?

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

const int MAXN = 400005;

struct node {
    int l, r, lc, rc, dat;
} tree[MAXN*4];
int top = 0, root = 0;
long long c1, c2;

int build(int l, int r, int pos)
{
    if (l == r) return tree[++top] = (node){l, r, 0, 0, 1}, top;
    int mid = (l+r)>>1, nd = ++top;
    tree[nd] = (node){l, r, 0, 0, 1};
    if (pos <= mid) return tree[nd].lc = build(l, mid, pos), nd;
    else return tree[nd].rc = build(mid+1, r, pos), nd;
}

int merge(int a, int b)
{
    if (a == 0 || b == 0) return a+b;
    // cerr << "Merging " << a << " " << b << endl;
    // display(a, 0), display(b, 0);
    c1 += (long long)tree[tree[a].lc].dat*tree[tree[b].rc].dat;
    c2 += (long long)tree[tree[b].lc].dat*tree[tree[a].rc].dat;
    tree[a].dat += tree[b].dat;
    tree[a].lc = merge(tree[a].lc, tree[b].lc);
    tree[a].rc = merge(tree[a].rc, tree[b].rc);
    return a;
}

int n;
long long cnt = 0, cp = 0;

int dfs()
{
    int nd; scanf("%d", &nd);
    if (nd != 0) return build(1, n, nd);
    ++cp;
    int t = cp;
    int x = dfs(), y = dfs();
    c1 = c2 = 0;
    int ret = merge(x, y);
    cnt += min(c1, c2);
    return ret;
}

int main()
{
    scanf("%d", &n);
    dfs();
    printf("%lld\n", cnt);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值