【算法竞赛】力扣杯春赛-战队赛 LCCUP‘23

力扣杯春赛 - 战队赛 LCCUP’23

符文储备

关键词:贪心

题目来源:LCP 77. 符文储备 - 力扣(Leetcode)

题目描述
 T贪心

远征队在出发前需要携带一些「符文」,作为后续的冒险储备。runes[i] 表示第 i 枚符文的魔力值。

他们将从中选取若干符文进行携带,并对这些符文进行重新排列,以确保任意相邻的两块符文之间的魔力值相差不超过 1

请返回他们能够携带的符文 最大数量

输入:runes = [1,3,5,4,1,7]
输出:3
解释:最佳的选择方案为[3,5,4]
将其排列为 [3,4,5] 后,任意相邻的两块符文魔力值均不超过 1,携带数量为 3
其他满足条件的方案为 [1,1] 和 [7],数量均小于 3。
因此返回可携带的最大数量 3。
输入:runes = [1,1,3,3,2,4]
输出:6
解释:排列为 [1,1,2,3,3,4],可携带所有的符文
数据范围
1 <= runes.length <= 10^4
0 <= runes[i] <= 10^4
问题分析

为了使得任意一块符文与其周围的魔力差值尽可能小,不妨将符文按照魔力值排序,从排序后的序列中选出满足“任意相邻的两块符文之间的魔力值相差不超过 1”的一段最长子序列,该子序列的长度即为题目要求的“最大数量”。

代码实现
int runeReserve(vector<int> &runes) {
    int n = runes.size(), res = 0, cur = 0;
    // 按魔法值从小到大排序
    sort(runes.begin(), runes.end());
    // 找出满足条件的最长序列
    for (int i = 1; i < n; i++) {
        if (runes[i] - runes[i - 1] <= 1)cur++;
        else res = max(cur, res), cur = 0;
    }
    res = max(res, cur);    // 不要漏掉最后一个子序列
    return res + 1;
}

时间复杂度:O(nlog(n))

空间复杂度:O(1)

城墙防线

关键词:二分

题目来源:LCP 78. 城墙防线 - 力扣(Leetcode)

题目描述
 T二分

在探险营地间,小扣意外发现了一片城墙遗迹,在探索期间,却不巧遇到迁徙中的兽群向他迎面冲来。情急之下小扣吹响了他的苍蓝笛,随着笛声响起,遗迹中的城墙逐渐发生了横向膨胀。
已知 rampart[i] = [x,y] 表示第 i 段城墙的初始所在区间。当城墙发生膨胀时,将遵循以下规则:

  • 所有的城墙会同时膨胀相等的长度;
  • 每个城墙可以向左、向右或向两个方向膨胀。

小扣为了确保自身的安全,需要在所有城墙均无重叠的情况下,让城墙尽可能的膨胀。请返回城墙可以膨胀的 最大值

注意:

  • 初始情况下,所有城墙均不重叠,且 rampart 中的元素升序排列;
  • 两侧的城墙可以向外无限膨胀。
输入:rampart = [[0,3],[4,5],[7,9]]
输出:3
解释:如下图所示:
rampart[0] 向左侧膨胀 3 个单位;
rampart[2] 向右侧膨胀 3 个单位;
rampart[1] 向左侧膨胀 1 个单位,向右膨胀 2 个单位。
不存在膨胀更多的方案,返回 3。
输入:rampart = [[1,2],[5,8],[11,15],[18,25]]
输出:4
数据范围
3 <= rampart.length <= 10^4
rampart[i].length == 2
0 <= rampart[i][0] < rampart[i][1] <= rampart[i+1][0] <= 10^8
问题分析

直观地来看,若膨胀长度为k时不发生重叠,那么膨胀长度为k-1时也不发生重叠,于是发现,k具有单调性,可通过二分来查找满足条件的最大的k。

设区间间隔的总和为total,则k的下限为0,k的上限为total/(n-2)。试想,按照最好的策略膨胀,第一个区间肯定全部往左边膨胀,最后一个区间肯定全部往右膨胀,那么还剩下中间n-1个区间间隔,而还有中间n-2个区间需要膨胀,这n-2个区间膨胀的总长度必定不能超过区间间隔的总和为total,也即每个区间最多能分到长度为total/(n-2)的间隔。

二分过程中,对k检查时,只需要检查中间n-2个区间即可,因为首尾两个区间肯定可以满足要求。在检查每个区间的过程中,只需要维护其左侧还剩多少空间,按照左侧尽量填满,左侧不够右侧来凑的策略进行填充。

代码实现
int rampartDefensiveLine(vector<vector<int>> &rampart) {
    // 测试数据中的区间已按左边界从小到大排序且不存在区间重叠的情况

    auto &v = rampart;
    int n = rampart.size(), l = 0, r, mid;
    for (int i = 1; i < n; i++)
        r += v[i][0] - v[i - 1][1];
    r /= n - 2;

    // 二分中的检查
    auto check = [&](int len) {
        // 当前检查的区间左侧的空间
        int ls = v[1][0] - v[0][1];
        for (int i = 1; i < n - 1; i++) {
            // 左侧够:全往左侧膨胀
            if (ls >= len)ls = v[i + 1][0] - v[i][1];
                // 左侧不够:左侧空间用完用右侧的
            else ls = v[i + 1][0] - v[i][1] - (len - ls);
            // 右侧不够用则无法完成膨胀
            if (ls < 0)return false;
        }
        return true;
    };

    // 二分答案
    while (l < r) {
        mid = (l + r + 1) >> 1;
        if (check(mid))l = mid;
        else r = mid - 1;
    }
    return l;
}

时间复杂度:O(nlog(m/n)),其中,n为区间数,m为区间间隔总和

空间复杂度:O(1)

提取咒文

关键词:宽度优先

题目来源:LCP 79. 提取咒文 - 力扣(Leetcode)

题目描述
 T宽度优先

随着兽群逐渐远去,一座大升降机缓缓的从地下升到了远征队面前。借由这台升降机,他们将能够到达地底的永恒至森。
在升降机的操作台上,是一个由魔法符号组成的矩阵,为了便于辨识,我们用小写字母来表示。 matrix[i][j] 表示矩阵第 ij 列的字母。该矩阵上有一个提取装置,可以对所在位置的字母提取。
提取装置初始位于矩阵的左上角 [0,0],可以通过每次操作移动到上、下、左、右相邻的 1 格位置中。提取装置每次移动或每次提取均记为一次操作。

远征队需要按照顺序,从矩阵中逐一取出字母以组成 mantra,才能够成功的启动升降机。请返回他们 最少 需要消耗的操作次数。如果无法完成提取,返回 -1

注意:

  • 提取装置可对同一位置的字母重复提取,每次提取一个
  • 提取字母时,需按词语顺序依次提取
问题分析

最短路问题一般可采用BFS来求解,由于本题可能会多次经过同一个位置,因此不能单纯使用位置来表示一个状态,状态还应该包含当前已经提取到多少个字符这一个信息。

于是,可采用一个三元组(i,j,k)来表示一个状态,(i,j,k)表示走到位置(i,j)时已经提取了k个字符(注意k的含义)。由于i、j、k均不超过100,因此可采用i、j、k分别可用一个6位的二进制数来表示,也即i、j、k可用一个整数来表示。

代码实现
int extractMantra(vector<string> &matrix, string mantra) {
    // 用三元组来表示状态
    typedef tuple<int, int, int> tp;
    // 往四个方向移动
    int dx[] = {0, 0, -1, 1}, dy[] = {-1, 1, 0, 0};

    const int N = 1e2 + 2;
    // 表示状态(i,j,k)是否已经出现过
    bool f[N][N][N] = {false};

    int m = matrix.size(), n = matrix[0].size(), l = mantra.size() - 1;

    // 两个数组模拟队列
    bool cur = false, nex = true;   // 标记哪个是当前层哪个是下一层
    int s[2] = {0};                 // 当前层的结点数和下一层的结点数
    tp q[2][N * N];                 // 当前层的结点和下一层的结点

    int step = 0, u, v;
    s[cur] = 1, q[cur][0] = {0, 0, 0}, f[0][0][0] = true;
    while (s[cur]) {
        while (s[cur]) {
            // 取出队头
            auto [i, j, k] = q[cur][--s[cur]];
            // 所在位置刚好是当前状态下一个要提取字符
            // 既然找到当前状态的下一个要提取的字符,那就没必要继续拓展当前结点了
            if (matrix[i][j] == mantra[k]) {
                if (k == l)return step + 1;
                if (!f[i][j][k + 1])
                    q[nex][s[nex]++] = {i, j, k + 1}, f[i][j][k + 1] = true;
                continue;   // 没必要继续拓展当前结点了
            }
            // 拓展当前结点
            for (int z = 0; z < 4; z++) {
                u = i + dx[z], v = j + dy[z];
                if (u < 0 || u >= m || v < 0 || v >= n || f[u][v][k])continue;
                q[nex][s[nex]++] = {u, v, k}, f[u][v][k] = true;
            }
        }
        // 操作数+1(不管前面一层的结点是拓展还是提取,操作均+1)
        step++, cur = !cur, nex = !nex;
    }
    return -1;
}

在代码中存在两个优化:

  • 使用两个数组来模拟队列,两个数组交替使用,节约空间。至于数组的第二维为什么开到N*N就够,暂时还没有想到比较好的证明方法。
  • 所在位置刚好是当前状态下一个要提取字符,那么直接提取即可,而不必拓展当前结点。直接提取肯定比从当前位置往后再找一个位置提取要快或者相等。证明也很简单,假设当前状态为p,当前状态所在位置恰好是下一个要提取的字符c,假设从p往后能找到一个比p更优的状态q,q状态所在位置也是字符c,从q提取c肯定不会比从p提取c更优,这与假设矛盾。

时间复杂度:O(mnl)。最坏情况下要遍历所有状态,实际上上述代码运行速度还是蛮快的,200ms内能跑完所有样例。

空间复杂度:O(mnl)

生物进化录

关键词:深度优先

题目来源:LCP 80. 生物进化录 - 力扣(Leetcode)

题目描述
 T深度优先

在永恒之森中,存在着一本生物进化录,以 一个树形结构 记载了所有生物的演化过程。经过观察并整理了各节点间的关系,parents[i] 表示编号 i 节点的父节点编号(根节点的父节点为 -1)。

为了探索和记录其中的演化规律,队伍中的炼金术师提出了一种方法,可以以字符串的形式将其复刻下来,规则如下:

  • 初始只有一个根节点,表示演化的起点,依次记录 01 字符串中的字符,
  • 如果记录 0,则在当前节点下添加一个子节点,并将指针指向新添加的子节点;
  • 如果记录 1,则将指针回退到当前节点的父节点处。

现在需要应用上述的记录方法,复刻下它的演化过程。请返回能够复刻演化过程的字符串中, 字典序最小01 字符串。

注意:

  • 节点指针最终可以停在任何节点上,不一定要回到根节点。
输入:parents = [-1,0,0,2]
输出:"00110"
解释:树结构如下图所示,共存在 2 种记录方案:。
第 1 种方案为:0(记录编号 1 的节点) -> 1(回退至节点 0) -> 0(记录编号 2 的节点) -> 0((记录编号 3 的节点))
第 2 种方案为:0(记录编号 2 的节点) -> 0(记录编号 3 的节点) -> 1(回退至节点 2) -> 1(回退至节点 0) -> 0(记录编号 1 的节点)
返回字典序更小的 "00110"
输入:parents = [-1,0,0,1,2,2]
输出:"00101100"
数据范围
1 <= parents.length <= 10^4
-1 <= parents[i] < i (即父节点编号小于子节点)
问题分析

对于“树”类的问题,时常和深度优先遍历相关,由于树结构的递归性,面对一个具体的问题时,首先要考虑该问题是否也可以递归求解。

在本题中,先处理两个细节。

一是,数据范围中有“父节点编号小于子节点”,说明结点0就是整棵树的根节点,因为比0小的数只有-1,父结点为-1的结点为根节点。

二是,题目中说“不一定要回到根节点”,不妨强制回到根节点,回到根节点的效果就是在序列的末尾多加了几个1,最后将这些1删除即可,后面会证明这样做并不影响答案。

设根节点为p,p的左右孩子结点为l、r(先假设只有两个孩子),那么,p树(以p为根节点的树)的最小01序列是否是由l树的最小01序列和r树的最小01序列组成呢?答案是肯定的。

首先明确一点,假设先遍历l树,肯定不会l树没遍历完,又回到根节点,再去遍历r树,再回到根节点,再遍历完l树,这样明显会增大字典序,因为要想减小字典序,就应该尽可能地使序列前面是0,也即尽可能往下走,所以,肯定是遍历完一棵子树再去遍历另一棵子树。

所以。p树的最小01序列必然是如下形式:0+l串+r串+1

对于任意一个结点(除整棵树的根节点)都存在一进一出,即从一个结点往下进入该结点,从该点往上出去,所以前面有1个0末尾有一个1。

那么,l串是l树的最小01序列吗?由于l串越小,0+l串+r串+1也就越小,故p树的最小01序列中的l串必定是l树的最小01序列,r串同理。l串r串,字典序小的在前面。

于是,求p树的最小01序列可转换为求l树的最小01序列和求r树的最小01序列,也即可将原问题转换为两个规模较小的且与原问题相似的且互不相关(影响)的子问题,从而可以递归求解。

当然,需要注意的是,题目并没有说每个结点只有两个子结点,前面只是为了方面描述。

现在,证明前面说的“这样做并不影响答案”,姑且将序列末尾的连续的一段1称为尾1。

由于要比较L串和R串的字典序,设一决胜负的点为K,可分为如下几种情况(只考虑L字典序小于R串字典序的情况)

  • K不在R串的尾1,即如下形式,其中A、B、C、D分别表示一段01序列,长度未必相同

    L串		A0C1111		# K不在L串的尾1
    R串		A1D11
    
    L串		A1			# K在L串的尾1
    R串		A11D111
    

    显然,去除LR的尾1后的LR的字典序小于去除RL的尾1后的RL的字典序,尽管LR的可能会更长,但LR和RL的决胜点在“A”后一个位置就确定了,与尾1无关。顺带说一句,尾1部分的前一个数必定不是1。

  • K在R串的尾1部分,即如下形式,

    L串		A101111111111	# K不在L串的尾1
    R串		A11111
    
    L串		A1111			# K在L串的尾1
    R串		A111111
    

    显然,也有,去除LR的尾1后的LR的字典序小于去除RL的尾1后的RL的字典序。

综上,若有L串字典序小于R串字典序成立,则必有(去尾1)LR串字典序小于(去尾1)RL串字典序,也即,在比较L、R串时,L、R带着尾1是不影响结果的。

代码实现
const int N = 1e4 + 5;
int h[N], e[N], ne[N], c[N], idx;

void add(int a, int b) {
    c[a]++, e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

string evolutionaryRecord(vector<int> &parents) {
    memset(h, -1, sizeof h);
    // 构建邻接表
    int n = parents.size();
    for (int i = 1; i < n; i++)add(parents[i], i);

    function<string(int)> dfs = [&](int u) {
        // 叶子结点:一进一出
        if (h[u]==-1)return string("01");
        // 非叶子结点:一进+遍历子树+一出
        // 子树的01序列
        int k = 0;
        string s[c[u]];
        for (int i = h[u]; ~i; i = ne[i])
            s[k++] = dfs(e[i]);
        // 子树01序列按字典序排列
        sort(s, s + k);
        // 拼接
        string r = "0";
        for (int i = 0; i < k; i++)r.append(s[i]);
        r.append("1");
        return r;
    };
    // dfs
    string res = dfs(0);
    //去除首位0和尾部1
    n = res.size();
    while (res[--n] == '1');

    return res.substr(1, n);
}

时间复杂度:介于O(n2)和O(nlog(n))之间

空间复杂度:O(n)

与非的谜题

关键词:线段树、位运算

题目来源:LCP 81. 与非的谜题 - 力扣(Leetcode)

题目描述
 T线段树
 T位运算

在永恒之森中,封存着有关万灵之树线索的卷轴,只要探险队通过最后的考验,便可以获取前往万灵之树的线索。

探险队需要从一段不断变化的谜题数组中找到最终的密码,初始的谜题为长度为 n 的数组 arr(下标从 0 开始),数组中的数字代表了 k 位二进制数。
破解谜题的过程中,需要使用 与非(NAND) 运算方式,operations[i] = [type,x,y] 表示第 i 次进行的谜题操作信息:

  • type = 0,表示修改操作,将谜题数组中下标 x 的数字变化为 y

  • type = 1,表示运算操作,将数字 y 进行 x*n 次「与非」操作,第 i 次与非操作为 y = y NAND arr[i%n]

    运算操作结果即:y NAND arr[0%n] NAND arr[1%n] NAND arr[2%n] ... NAND arr[(x*n-1)%n]

最后,将所有运算操作的结果按顺序逐一进行 异或(XOR)运算,从而得到最终解开封印的密码。请返回最终解开封印的密码。

「与非」(NAND)的操作为:先进行 操作,后进行 操作。

输入:
k = 3
arr = [1,2]
operations = [[1,2,3],[0,0,3],[1,2,2]]
输出: 2
输入:
k = 4
arr = [4,6,4,7,10,9,11]
operations = [[1,5,7],[1,7,14],[0,6,7],[1,6,5]]
输出: 9
数据范围
1 <= arr.length, operations.length <= 10^4
1 <= k <= 30
0 <= arr[i] < 2^k
若 type = 0,0 <= x < arr.length 且 0 <= y < 2^k
若 type = 1,1 <= x < 10^9 且 0 <= y < 2^k
保证存在 type = 1 的操作
问题分析

初看,存在单点修改和区间查询,大概和树状数组、线段树有关。

由于NAND运算不满足结合律,所以,没有[i…j]NAND值=[i…k]NAND值 NAND[k+1…j]NAND值成立,也即不能单纯维护某段区间的NAND值。

y NAND arr[0] NAND ... NAND arr[n-1]可看做y从数组穿过,但穿过a[i]后的值与前面的a[j]都有关系,其中j<i,由于y可用二进制数表示,不妨先考虑1位二进制(要么是0,要么是1)。

维护0从[i…k]穿过后的值l[0],1从[i…k]穿过后的值l[1],对于区间[k+1…j]同理,得r[0]和r[1],于是,0从区间[i…j]穿过后的值就为r[l[0]],0从区间[i…j]穿过后的值就为r[l[1]]。由于y有k位,因此,需要维护0从区间[i…j]的第0位穿过后的值,从区间[i…j]的第1位穿过后的值,…,1同理。

对于0/1穿过区间[i…j]后的值,可使用线段树来维护。

  • 执行修改操作时,所有包含修改点的区间维护的值均需要修改(重新计算)
  • 执行查询操作时,只需要查询0/1穿过区间[0…n-1]后的值即可,可以省略查询函数。

由于需要多次穿过区间,需要对穿过次数进行分类讨论,设穿过次数为x,现在先只看一位二进制y,假设其穿过1次后变成y1,穿过2次后变成y1,则有

  • 若x=1,那么结果就为y1;
  • 若y1=y,也即穿1次后不变,可以想象得到,无论穿过多少次都是不变的,于是结果仍为y1
  • 若y2=y1,说明,从y1开始,无论穿过多少次仍为y1,于是结果为y1
  • 否则,说明y1!=y,y2!=y1,又由于取值只有0和1,所以是01交替出现,若x为奇数,则结果为y1,若x为偶数,则结果为y(不变),且y和y1不同(一个0,一个1),于是结果可写作y^(x&1)
代码实现
const int N = 1e4 + 5;

struct Node {
    int l, r, f[2];
} tr[N << 2];
int *a, M, K;     // 数组 mask K

/**
 * 更新节点信息
 */
void pushup(int u) {
    auto &t = tr[u].f, &l = tr[u << 1].f, &r = tr[u << 1 | 1].f;
    t[0] = 0, t[1] = 0;
    for (int i = 0; i < K; i++) {
        // &(1<<i)表示只看当前第i位(倒数)
        t[0] |= r[l[0] >> i & 1] & (1 << i);
        t[1] |= r[l[1] >> i & 1] & (1 << i);
    }
}

/**
 * 建立线段树
 */
void build(int u, int l, int r) {
    tr[u] = {l, r};
    // 叶子节点
    if (l == r) {
        tr[u].f[0] = M, tr[u].f[1] = ~*(a + l);
        return;
    }
    // 递归创建节点
    int mid = (l + r) >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
    // 更新节点u的f
    pushup(u);
}

/**
 * 修改操作
 */
void modify(int u, int x, int v) {
    // 找到修改位置(叶子结点只修改1即可)
    if (tr[u].l == x && tr[u].r == x)tr[u].f[1] = ~v;
    else {
        // 去左区间或者右区间找
        int mid = (tr[u].l + tr[u].r) >> 1;
        if (x <= mid)modify(u << 1, x, v);
        else modify(u << 1 | 1, x, v);
        // 更新节点u的f
        pushup(u);
    }
}


int getNandResult(int k, vector<int> &arr, vector<vector<int>> &operations) {
    int n = arr.size(), res = 0;
    a = &arr[0], M = (1 << k) - 1, K = k;
    // 建立线段树
    build(1, 0, n - 1);
    // 执行操作
    int r, y, t;
    for (auto &op: operations) {
        // 运算操作
        if (op[0]) {
            for (int i = 0; i < K; i++) {
                y = op[2] >> i & 1;
                // 先穿过一次
                t = tr[1].f[y] >> i & 1;
                // 若只穿过1次,则取穿过1次的结果
                // 若穿过1次的结果与原来相同,则不论穿过多少次,都会与原来相同,故取穿过1次的结果
                // 若穿过2次的结果与穿过1次的结果相同,则再往后穿,结果与第一次也相同,故取穿过1次的结果
                // 以上条件由上到下优先级依次降低
                // 否则,说明原来与第一次结果不同,第一次与第二次不同,也即01交替
                // 若为偶数次,则保持不变,若为奇数次,则与第一次相同
                if (op[1] != 1 && t != y && (tr[1].f[t] >> i & 1) != t)
                    // t = op[1] & 1 ? t : y;
                    t = y ^ (op[1] & 1);
                res ^= t << i;
            }
        }
        // 修改操作
        else modify(1, op[1], op[2]);
    }
    return res;
}

时间复杂度:O( log(n) + m( k+log(n) ) ) ≈ O(mk)

空间复杂度:O(n)

万灵之树

关键词:深度优先、动态规划、状态压缩、数学

题目来源:LCP 82. 万灵之树 - 力扣(Leetcode)

题目描述
 T深度优先
 T动态规划
 T状态压缩
 T数学

探险家小扣终于来到了万灵之树前,挑战最后的谜题。
已知小扣拥有足够数量的链接节点和 n 颗幻境宝石,gem[i] 表示第 i 颗宝石的数值。现在小扣需要使用这些链接节点和宝石组合成一颗二叉树,其组装规则为:

  • 链接节点将作为二叉树中的非叶子节点,且每个链接节点必须拥有 2 个子节点;
  • 幻境宝石将作为二叉树中的叶子节点,所有的幻境宝石都必须被使用。

能量首先进入根节点,而后将按如下规则进行移动和记录:

  • 若能量首次到达该节点时:
    • 记录数字 1
    • 若该节点为叶节点,将额外记录该叶节点的数值;
  • 若存在未到达的子节点,则选取未到达的一个子节点(优先选取左子节点)进入;
  • 若无子节点或所有子节点均到达过,此时记录 9,并回到当前节点的父节点(若存在)。

如果最终记下的数依序连接成一个整数 num,满足num mod   p= target,则视为解开谜题。
请问有多少种二叉树的组装方案,可以使得最终记录下的数字可以解开谜题

注意:

  • 两棵结构不同的二叉树,作为不同的组装方案
  • 两棵结构相同的二叉树且存在某个相同位置处的宝石编号不同,也作为不同的组装方案
  • 可能存在数值相同的两颗宝石
输入:gem = [2,3]
p = 100000007
target = 11391299
输出:1
输入:gem = [3,21,3]
p = 7
target = 5
输出:4
数据范围
1 <= gem.length <= 9
0 <= gem[i] <= 10^9
1 <= p <= 10^9,保证 
p 为素数。
0 <= target < p
存在 2 组 gem.length == 9 的用例
问题分析

参考大佬题解:hqztrue

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值