CSP CCF: 201812-3 CIDR合并 (C++)

题目来源

CIDR合并

数据特点

在这里插入图片描述

题目提示

在这里插入图片描述

知识点

  1. 整数与字符串相互转换

    <stdlib.h>
    
    整数转字符串: 
    itoa (表示 integer to alphanumeric)是把整型数转换成字符串的一个函数。
    char* itoa(int value,char*string,int radix);// value: 要转换的整数,string: 转换后的字符串,radix: 转换进制数,如2,8,10,16 进制等。```
    
    字符转整数:
     atoi (表示 alphanumeric to integer)是把字符串转换成整型数的一个函数
    int  atoi(const char *nptr);//字符串转整数函数,nptr: 要转换的字符串
    
  2. 32位无符号整数: uint32_t

     uint32_t 即 unsigned int 
     
     https://blog.csdn.net/mary19920410/article/details/71518130
     	_t 表示这些数据类型是通过typedef定义的,而不是新的数据类型。也就是说,它们其实是我们已知的类型的别名。
     	typedef unsigned int            uint32_t;
     	typedef signed char             int8_t;
     	typedef short int               int16_t;
     	typedef int                     int32_t;
     	typedef long int                int64_t; 
     	typedef long long int           int64_t;
    

0 分

只处理标准型IP前缀,且只进行排序。无合并

#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <stdlib.h>  // itoa,
#include <algorithm>

using namespace std;

vector<pair<int, int> > ips;  // [{pref, len}, {pref, len}, ... ]  pref: ip地址

bool cmp(pair<int, int> p1, pair<int, int> p2) {
    if (p1.first != p2.first) {  // 比较 ip地址/pref 大小
        return p1.first < p2.first;
    }
    else {  // 比较 len 大小
        return p1.second < p2.second;
    }
}


int main() {

    ifstream cin("in3.txt");

    int N;  // N <= 10^6
    cin>>N;

    for (int i = 0; i < N; ++i) {
        string cur;
        cin>>cur;  // 标准型、 省略后缀型、 省略长度型    !合法的!

        // 处理 ip前缀为 [{32位无符号整数, length}, {}, ...]。
        // 标准型:先计算ip地址pref为十进制整数, 再计算 len;
        // 省略后缀型: 注意ip地址中(3 - dotnum)的'.0', 其余和标准型一致;
        // 省略长度型: 注意ip地址中(3 - dotnum)的'.0' 和 '/len', 其余和标准型一致。
        int ip = 0;
        int num = 0;
        
        // 只处理标准型的ip
        for (int j = 0; j < cur.size(); ++j) {
            char c = cur[j];
            if (c == '.'|| c == '/') {
                ip = ip * 256 + num;
                num = 0;
            }
            else {
                num = num * 10 + c - '0';
            }
        }
        ips.push_back(pair<int, int>{ip, num});
    }


    // 排序
    sort(ips.begin(), ips.end(), cmp);


    // 合并1

    // 合并2

    // ip由 {pref(整数), len} 变为 a3.a2.a1.a0/len ; 输出答案。
    for (pair<int , int > ip: ips) {
        // 转换
        int a[4] = {0};
        int indexa = 0;

        int tmpip = ip.first;
        while (tmpip != 0) {
            a[indexa++] = tmpip % 256;
            tmpip = tmpip / 256;
        }

        // 输出
        cout<<a[3]<<'.'<<a[2]<<'.'<<a[1]<<'.'<<a[0]<<'/'<<ip.second<<endl;
    }

    return 0;
}

40 分

就这20分来写的,得了40分。
将0分代码int改为32位无符号整数 uint32_t。

#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <stdlib.h>  // itoa,
#include <algorithm>

using namespace std;

vector<pair<uint32_t, int> > ips;  // [{pref, len}, {pref, len}, ... ]  pref: ip地址

bool cmp(pair<uint32_t, int> p1, pair<uint32_t, int> p2) {
    if (p1.first != p2.first) {  // 比较 ip地址/pref 大小
        return p1.first < p2.first;
    }
    else {  // 比较 len 大小
        return p1.second < p2.second;
    }
}


int main() {

    ifstream cin("in3.txt");

    int N;  // N <= 10^6
    cin>>N;

    for (int i = 0; i < N; ++i) {
        string cur;
        cin>>cur;  // 标准型、 省略后缀型、 省略长度型    !合法的!

        // 处理 ip前缀为 [{32位无符号整数, length}, {}, ...]。
        // 标准型:先计算ip地址pref为十进制整数, 再计算 len;
        // 省略后缀型: 注意ip地址中(3 - dotnum)的'.0', 其余和标准型一致;
        // 省略长度型: 注意ip地址中(3 - dotnum)的'.0' 和 '/len', 其余和标准型一致。
        uint32_t ip = 0;
        int num = 0;

  
        // 只处理标准型的ip
        for (int j = 0; j < cur.size(); ++j) {
            char c = cur[j];
            if (c == '.'|| c == '/') {
                ip = ip * 256 + num;
                num = 0;
            }
            else {
                num = num * 10 + c - '0';
            }
        }
        ips.push_back(pair<uint32_t, int>{ip, num});
    }


    // 排序
    sort(ips.begin(), ips.end(), cmp);


    // 合并1

    // 合并2

    // ip由 {pref(整数), len} 变为 a3.a2.a1.a0/len ; 输出答案。
    for (pair<uint32_t , int > ip: ips) {
        // 转换
        int a[4] = {0};
        int indexa = 0;

        uint32_t tmpip = ip.first;
        while (tmpip != 0) {
            a[indexa++] = tmpip % 256;
            tmpip = tmpip / 256;
        }

        // 输出
        cout<<a[3]<<'.'<<a[2]<<'.'<<a[1]<<'.'<<a[0]<<'/'<<ip.second<<endl;
    }

    return 0;
}

40分

添加 处理非标准型IP前缀的方法。

#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <stdlib.h>  // itoa,
#include <algorithm>

using namespace std;

vector<pair<uint32_t, int> > ips;  // [{pref, len}, {pref, len}, ... ]  pref: ip地址

bool cmp(pair<uint32_t, int> p1, pair<uint32_t, int> p2) {
    if (p1.first != p2.first) {  // 比较 ip地址/pref 大小
        return p1.first < p2.first;
    }
    else {  // 比较 len 大小
        return p1.second < p2.second;
    }
}


int main() {

    ifstream cin("in2.txt");

    int N;  // N <= 10^6
    cin>>N;

    for (int i = 0; i < N; ++i) {
        string cur;
        cin>>cur;  // 标准型、 省略后缀型、 省略长度型    !合法的!

        // 处理 ip前缀为 [{32位无符号整数, length}, {}, ...]。
        // 标准型:先计算ip地址pref为十进制整数, 再计算 len;
        // 省略后缀型: 注意ip地址中(3 - dotnum)的'.0', 其余和标准型一致;
        // 省略长度型: 注意ip地址中(3 - dotnum)的'.0' 和 '/len', 其余和标准型一致。
        uint32_t ip = 0;
        int len = 8;
        string standard = "";
        int dotnum = 0;
        int xienum = 0;
        int num = 0;

        for (int j = 0; j < cur.size(); ++j) {
            char c = cur[j];
            if (c == '.') {
                ++dotnum;  // 记录 '.' 数量
                len += 8;
                ip = ip * 256 + num;
                num = 0;
            }
            else if (c == '/' || (j == cur.size() - 1 && dotnum == 0)) {  // 遇到 '/' or 是省略长度型且只有一段
                if (c == '/') {
                    ++xienum;
                }
                else {
                    num = num * 10 + c - '0';
                }

                while (dotnum != 3) {  // 补充ip地址中(3 - dotnum)的'.0'
                    ip = ip * 256 + num;
                    num = 0;
                    ++dotnum;
                }
                ip = ip * 256 + num;

                num = 0;

            }
            else {
                num = num * 10 + c - '0';
            }
        }
        if (xienum == 0) {  // 省略长度型
            ips.push_back(pair<uint32_t, int>{ip, len});
        }
        else {
            ips.push_back(pair<uint32_t, int>{ip, num});
        }
    }


    // 排序
    sort(ips.begin(), ips.end(), cmp);


    // 合并1

    // 合并2

    // ip由 {pref(整数), len} 变为 a3.a2.a1.a0/len ; 输出答案。
    for (pair<uint32_t , int > ip: ips) {
        // 转换
        int a[4] = {0};
        int indexa = 0;

        uint32_t tmpip = ip.first;
        while (tmpip != 0) {
            a[indexa++] = tmpip % 256;
            tmpip = tmpip / 256;
        }

        // 输出
        cout<<a[3]<<'.'<<a[2]<<'.'<<a[1]<<'.'<<a[0]<<'/'<<ip.second<<endl;
    }

    return 0;
}


80分

完成 排序、 从小到大合并

#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <stdlib.h>  // itoa,
#include <algorithm>

using namespace std;


bool cmp(pair<uint32_t, int> p1, pair<uint32_t, int> p2) {
    if (p1.first != p2.first) {  // 比较 ip地址/pref 大小
        return p1.first < p2.first;
    }
    else {  // 比较 len 大小
        return p1.second < p2.second;
    }
}


bool isSubSet(uint32_t a, uint32_t b, int alen, int blen) {  // ip前缀b是ip前缀a的子集
    int aa[32] = {0}, bb[32] = {0};   // 存储a、b的二进制

    uint32_t tmpa = a, tmpb = b;
    int indexa = 31, indexb = 31;
    while (tmpa != 0) {
        aa[indexa--] = tmpa % 2;
        //cout<<tmpa%2<<endl;
        tmpa = tmpa / 2;
    }
    while (tmpb != 0) {
        bb[indexb--] = tmpb % 2;
        tmpb = tmpb / 2;
    }

    for (int i = 0; i < min(alen, blen); ++i) {
        if (bb[i] != aa[i]) {
            return false;
        }
    }

    return true;
}


bool isOK(uint32_t aa, int aalen, uint32_t a, int alen, uint32_t b, int blen) {
    int aa2[32] = {0}, a2[32] = {0}, b2[32] = {0};   // 存储aa的二进制
    uint32_t tmpaa = aa, tmpa = a, tmpb = b;
    int indexaa = 31, indexa = 31, indexb = 31;

    // aa 是不是合法的
    while (tmpaa != 0) {
        aa2[indexaa--] = tmpaa % 2;
        tmpaa = tmpaa / 2;
    }
    for (int i = aalen; i < 32; ++i) {
        if (aa2[i] == 1) {
            return false;
        }
    }

    // 获取 a, b的二进制
    while (tmpa != 0) {
        a2[indexa--] = tmpa % 2;
        tmpa = tmpa / 2;
    }
    while (tmpb != 0) {
        b2[indexb--] = tmpb % 2;
        tmpb = tmpb / 2;
    }

    // 判断 a + b == aa
    if ((a2[aalen] == 0 && b2[aalen] == 1) || (a2[aalen] == 1 && b2[aalen] == 0)) {
        return true;
    }
    else {
        return false;
    }
}

int main() {

    ifstream cin("in4.txt");

    int N;  // N <= 10^6
    cin>>N;

    vector<pair<uint32_t, int> > ips;  // [{pref, len}, {pref, len}, ... ]  pref: ip地址
    for (int i = 0; i < N; ++i) {
        string cur;
        cin>>cur;  // 标准型、 省略后缀型、 省略长度型    !合法的!

        // 处理 ip前缀为 [{32位无符号整数, length}, {}, ...]。
        // 标准型:先计算ip地址pref为十进制整数, 再计算 len;
        // 省略后缀型: 注意ip地址中(3 - dotnum)的'.0', 其余和标准型一致;
        // 省略长度型: 注意ip地址中(3 - dotnum)的'.0' 和 '/len', 其余和标准型一致。
        uint32_t ip = 0;
        int len = 8;
        string standard = "";
        int dotnum = 0;
        int xienum = 0;
        int num = 0;

        for (int j = 0; j < cur.size(); ++j) {
            char c = cur[j];
            if (c == '.') {
                ++dotnum;  // 记录 '.' 数量
                len += 8;
                ip = ip * 256 + num;
                num = 0;
            }
            else if (c == '/' || (j == cur.size() - 1 && dotnum == 0)) {  // 遇到 '/' or 是省略长度型且只有一段
                if (c == '/') {
                    ++xienum;
                }
                else {
                    num = num * 10 + c - '0';
                }

                while (dotnum != 3) {  // 补充ip地址中(3 - dotnum)的'.0'
                    ip = ip * 256 + num;
                    num = 0;
                    ++dotnum;
                }
                ip = ip * 256 + num;

                num = 0;

            }
            else {
                num = num * 10 + c - '0';
                if (j == cur.size() - 1 && xienum == 0 && dotnum != 0) {  // 省略长度型, 且不是一段
                    while (dotnum != 3) {  // 补充ip地址中(3 - dotnum)的'.0'
                        ip = ip * 256 + num;
                        num = 0;
                        ++dotnum;
                    }
                    ip = ip * 256 + num;

                    num = 0;
                }
            }
        }
        if (xienum == 0) {  // 省略长度型
            ips.push_back(pair<uint32_t, int>{ip, len});
        }
        else {
            ips.push_back(pair<uint32_t, int>{ip, num});
        }
    }


    // 排序
    sort(ips.begin(), ips.end(), cmp);


    // 从小到大合并,
    pair<uint32_t, int> ips2[N];
    int num_ips2 = 0;
    int index1 = 0, index2 = 1;
    while (index2 < N) {
        uint32_t a = ips[index1].first, b = ips[index2].first;
        int alen = ips[index1].second, blen = ips[index2].second;
        if (isSubSet(a, b, alen, blen)) {  // ip前缀b是ip前缀a的子集, 删除b, 往后移
            if (index2 == N - 1) {  // 到达最后一个ip了, 但是b是a的子集
                ips2[num_ips2++] = ips[index1];
            }
            ++index2;
        }
        else {
            ips2[num_ips2++] = ips[index1];
            if (index2 == N - 1) {  // 到达最后一个ip了, b不是a的子集
                ips2[num_ips2++] = ips[index2];
            }
            index1 = index2;
            ++index2;
        }
    }
  

    // ip由 {pref(整数), len} 变为 a3.a2.a1.a0/len ; 输出答案。
    for (int i = 0; i < num_ips2; ++i) {  // <=newnum  <num_ips2
        pair<uint32_t, int> ip = ips2[i];
        // 转换
        int a[4] = {0};
        int indexa = 0;

        uint32_t tmpip = ip.first;
        while (tmpip != 0) {
            a[indexa++] = tmpip % 256;
            tmpip = tmpip / 256;
        }

        // 输出
        cout<<a[3]<<'.'<<a[2]<<'.'<<a[1]<<'.'<<a[0]<<'/'<<ip.second<<endl;
    }


    return 0;
}

100分

添加了同级合并后, 总是30分, 后来发现是 isOK()函数中 判断a并b是否等于a'时没写判断前len-1项是否相等了。

#include <iostream>
//#include <fstream>
#include <vector>
#include <map>
//#include <stdlib.h>  // itoa,
#include <algorithm>
#include <stack>

using namespace std;


bool cmp(pair<uint32_t, int> p1, pair<uint32_t, int> p2) {
    if (p1.first != p2.first) {  // 比较 ip地址/pref 大小
        return p1.first < p2.first;
    }
    else {  // 比较 len 大小
        return p1.second < p2.second;
    }
}


// 判断ip前缀b是否是ip前缀a的子集
bool isSubSet(uint32_t a, uint32_t b, int alen, int blen) { 
    int aa[32] = {0}, bb[32] = {0};   // 存储a、b的二进制

    uint32_t tmpa = a, tmpb = b;
    int indexa = 31, indexb = 31;
    while (tmpa != 0) {
        aa[indexa--] = tmpa % 2;
        tmpa = tmpa / 2;
    }
    while (tmpb != 0) {
        bb[indexb--] = tmpb % 2;
        tmpb = tmpb / 2;
    }

    for (int i = 0; i < min(alen, blen); ++i) {
        if (bb[i] != aa[i]) {
            return false;
        }
    }

    return true;
}


// 判断 aa (即a')是否合法 以及 a并b是否等于 aa(即a')
bool isOK(uint32_t aa, int aalen, uint32_t a, int alen, uint32_t b, int blen) {
    if (aalen < 0) {  
        return false;
    }

    int aa2[32] = {0}, a2[32] = {0}, b2[32] = {0};   // 存储aa的二进制
    uint32_t tmpaa = aa, tmpa = a, tmpb = b;  // 存储 32位无符号整型
    int indexaa = 31, indexa = 31, indexb = 31;  // 记录下标

    // aa 是不是合法的
    while (tmpaa != 0) {
        aa2[indexaa--] = tmpaa % 2;
        tmpaa = tmpaa / 2;
    }
    /*
    // 改为下边的 if 更简单
    for (int i = aalen; i < 32; ++i) {
        if (aa2[i] == 1) {
            return false;
        }
    }
    */
    if (aa2[aalen] == 1) {
        return false;
    }

    // 获取 a, b的二进制
    while (tmpa != 0) {
        a2[indexa--] = tmpa % 2;
        tmpa = tmpa / 2;
    }
    while (tmpb != 0) {
        b2[indexb--] = tmpb % 2;
        tmpb = tmpb / 2;
    }

    // 判断 a 、 b 前 len - 1位是否相等    !!!! 这里给忘了!!!
    for (int i = 0; i < aalen; ++i) {
        if (a2[i] != b2[i]) {
            return false;
        }
    }
    
	// 判断 a + b == aa
    if ((a2[aalen] == 0 && b2[aalen] == 1) || (a2[aalen] == 1 && b2[aalen] == 0) ) {
        return true;
    }
    else {
        return false;
    }
}

int main() {

    //ifstream cin("in4.txt");

    int N;  // N <= 10^6
    cin>>N;

    vector<pair<uint32_t, int> > ips;  // [{pref, len}, {pref, len}, ... ]  pref: ip地址
    for (int i = 0; i < N; ++i) {
        string cur;
        cin>>cur;  // 标准型、 省略后缀型、 省略长度型    !合法的!

        // 处理 ip前缀为 [{32位无符号整数, length}, {}, ...]。
        // 标准型:先计算ip地址pref为十进制整数, 再计算 len;
        // 省略后缀型: 注意ip地址中(3 - dotnum)的'.0', 其余和标准型一致;
        // 省略长度型: 注意ip地址中(3 - dotnum)的'.0' 和 '/len', 其余和标准型一致。
        uint32_t ip = 0;
        int len = 8;
        string standard = "";
        int dotnum = 0;
        int xienum = 0;
        int num = 0;

        for (int j = 0; j < cur.size(); ++j) {
            char c = cur[j];
            if (c == '.') {
                ++dotnum;  // 记录 '.' 数量
                len += 8;
                ip = ip * 256 + num;
                num = 0;
            }
            else if (c == '/' || (j == cur.size() - 1 && dotnum == 0)) {  // 遇到 '/' or 是省略长度型且只有一段
                if (c == '/') {
                    ++xienum;
                }
                else {
                    num = num * 10 + c - '0';
                }

                while (dotnum != 3) {  // 补充ip地址中(3 - dotnum)的'.0'
                    ip = ip * 256 + num;
                    num = 0;
                    ++dotnum;
                }
                ip = ip * 256 + num;

                num = 0;

            }
            else {
                num = num * 10 + c - '0';
                if (j == cur.size() - 1 && xienum == 0 && dotnum != 0) {  // 省略长度型, 且不是一段
                    while (dotnum != 3) {  // 补充ip地址中(3 - dotnum)的'.0'
                        ip = ip * 256 + num;
                        num = 0;
                        ++dotnum;
                    }
                    ip = ip * 256 + num;

                    num = 0;
                }
            }
        }
        if (xienum == 0) {  // 省略长度型
            ips.push_back(pair<uint32_t, int>{ip, len});
        }
        else {
            ips.push_back(pair<uint32_t, int>{ip, num});
        }
    }


    // 排序
    sort(ips.begin(), ips.end(), cmp);


    // 从小到大合并
    pair<uint32_t, int> ips2[N];
    int num_ips2 = 0;
    int index1 = 0, index2 = 1;
    while (index2 < N) {
        uint32_t a = ips[index1].first, b = ips[index2].first;
        int alen = ips[index1].second, blen = ips[index2].second;
        if (isSubSet(a, b, alen, blen)) {  // ip前缀b是ip前缀a的子集, 删除b, 往后移
            if (index2 == N - 1) {  // 到达最后一个ip了, 但是b是a的子集
                ips2[num_ips2++] = ips[index1];
            }
            ++index2;
        }
        else {
            ips2[num_ips2++] = ips[index1];
            if (index2 == N - 1) {  // 到达最后一个ip了, b不是a的子集
                ips2[num_ips2++] = ips[index2];
            }
            index1 = index2;
            ++index2;
        }
    }


    // 同级合并  101.6.6.0/24   101.6.7.0/24
    bool govector = true; //
    stack<pair<uint32_t, int> > mystack;
    int index = 1;  // ips2的下标
    mystack.push(ips2[0]);

    while (index < num_ips2) {
        if (govector) {  // 推进ips2 
            uint32_t aa, a, b;
            int aalen, alen, blen;

            b = ips2[index].first;
            blen = ips2[index].second;

            aa = a = mystack.top().first;
            alen = mystack.top().second;
            aalen = alen - 1;
            mystack.pop();

            if (alen == blen && isOK(aa, aalen, a, alen, b , blen)) {  // 前缀长度相同 且 能合并,合并后推进ips2/处理 mystack
                mystack.push(pair<uint32_t, int>{aa, aalen});  // 合并

                if (mystack.size() == 1) {  // 前边没有元素, 推进 ips2
                    ++index;
                    govector = true;
                    //cout<<"*1"<<endl;
                }
                else {  // 前边有元素, 处理 mystack
                    ++index;
                    govector = false;
                    //cout<<"*2"<<endl;
               }
            }
            else {  // 不能合并,  推进 ips2
                ++index;
                govector = true;
                mystack.push(pair<uint32_t, int>{a, alen});
                mystack.push(pair<uint32_t, int>{b, blen});  // 合并
                //cout<<"*3"<<endl;
            }
        }
        else {  // 处理 mystack
            uint32_t aa, a, b;
            int aalen, alen, blen;
            b = mystack.top().first;
            blen = mystack.top().second;
            mystack.pop();
            aa = a = mystack.top().first;
            alen = mystack.top().second;
            aalen = alen - 1;
            mystack.pop();

            if (alen == blen && isOK(aa, aalen, a, alen, b , blen)) {  // 前缀长度相同 且 能合并, 合并后往前走/往后走
                mystack.push(pair<uint32_t, int>{aa, aalen});  // 合并

                if (mystack.size() == 1) {  // 前边没有元素,推进 ips2
                    govector = true;
                    //cout<<"*4"<<endl;
                }
                else {  // 前边有元素, 处理 mystack
                    govector = false;
                    //cout<<"*5"<<endl;
                }
            }
            else {  // 不能合并, 推进 ips2
                govector = true;
                mystack.push(pair<uint32_t, int>{a, alen});
                mystack.push(pair<uint32_t, int>{b, blen});  // 合并
                //cout<<"*6"<<endl;
            }
        }
    }

    // 从stack中获得答案
    int anssize = mystack.size();
    pair<uint32_t, int> ans[anssize];
    for (int i = anssize - 1; i >= 0; --i) {
        ans[i] = mystack.top();
        mystack.pop();
    }

    // ip由 {pref(整数), len} 变为 a3.a2.a1.a0/len ; 输出答案。
    for (int i = 0; i < anssize; ++i) {  // <=newnum  <num_ips2
        pair<uint32_t, int> ip = ans[i];
        // 转换
        int a[4] = {0};
        int indexa = 0;
        uint32_t tmpip = ip.first;
        while (tmpip != 0) {
            a[indexa++] = tmpip % 256;
            tmpip = tmpip / 256;
        }

        // 输出
        cout<<a[3]<<'.'<<a[2]<<'.'<<a[1]<<'.'<<a[0]<<'/'<<ip.second<<endl;
    }


    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值