【2024.8.16】Traveller解题报告——图论基础

写在前面

前段时间做多校,感觉自己的基础不太好,之前学过的一些东西也都忘记了,想趁这个机会回来补一补基础,尤其是图论这一块。因此我打算回来做一些经典的图论算法题,“不积跬步,无以至千里”,还是从基础的开始,一步一步去挑战更难的题目。

HDU - 4081 Qin Shi Huang’s National Road System

题目大意

给你一张 n n n个点的二维平面图,每个点都有其坐标和点权(第 i i i个点的点权为 a i a_i ai),要求一棵连接这 n n n个点的生成树,满足其中一条边连接的两个点的点权之和与除去这条边以外 n − 2 n-2 n2条边的边权和的比值最大。在本题中边权即为两点之间的欧几里得距离。

数据范围: 3 ≤ n ≤ 1 0 3 3 \le n \le 10^3 3n103(题目说 n ≥ 2 n \ge 2 n2 n n n显然不可能为2), 1 ≤ a i ≤ 1 0 5 1 \le a_i \le 10^5 1ai105

解题思路

首先,我们称那条不算边权的边为魔法边。这道题其实一上来就应该想到去枚举最小生成树的每一条边。为什么这么说?因为最小生成树上的每一条边的边权都是最小的,如果最终的边除了魔法边外不在最小生成树上,那么其边权之和一定会大于最小生成树生成的边。最小生成树足够把 n n n个点连接起来。

那么答案就呼之欲出了。首先跑出最小生成树,然后枚举每条最小生成树上的边,去掉该条边,则图会被分割成两个不连通的树。这时分别用dfs找到这两棵树上点权最大的点,用魔法边连接这两个点,更新答案。时间复杂度为 O ( n 2 ) O(n^2) O(n2),可以通过此题。

AC代码

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define pb push_back

#define Yes cout << "Yes\n"
#define No cout << "No\n"
#define YES cout << "YES\n"
#define NO cout << "NO\n"
#define ff first
#define ss second
#define fori(x,y) for(int i=x;i<=(int)(y);++i)
#define forj(x,y) for(int j=x;j<=(int)(y);++j)
#define fork(x,y) for(int k=x;k<=(int)(y);++k)

#define debug(x) cout << #x << " = " << x << endl

typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;

ll MOD = 998244353;
ll qpow(ll a,ll p) {ll res=1; while(p) {if (p&1) {res=res*a%MOD;} a=a*a%MOD; p>>=1;} return res;}

const int N = 1e3+7;

int x[N], y[N], arr[N];

double getdis(int i, int j) {
    return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}

vector<int> g[N];
set<pii> edge;
bool done[N];  // done[i] = true表示该点已经在MST中
double prim(int n) {
    int s=1;  // 从任意一个顶点开始
    fori(1, n) done[i] = false;  // 初始化
    priority_queue<pair<double,pii>, vector<pair<double,pii>>, greater<pair<double,pii>>> que;
    que.push({0, {s, 0}});
    int cnt = 0;  // 记录顶点个数而非边数!
    double ans = 0;  // 记录最小权值
    while (que.size()) {
        auto pi = que.top();
        int u = pi.second.first;
        que.pop();
        if (done[u]) continue;  // 判圈
        done[u] = true;
        ans += pi.first;
        cnt++;

        // addedge
        if (pi.second.second) {
            edge.insert({pi.second.second, u});
            g[pi.second.second].push_back(u);
            g[u].push_back(pi.second.second);
        }

        fori(1, n) {
            if (done[i]) continue;
            double dis = getdis(u, i);
            que.push({dis, {i, u}});
        }
    }
    return ans;
}

int ma = 0;
void dfs(int u, int fa) {
    if (arr[u]>arr[ma]) ma = u;
    for (auto v: g[u]) {
        if (v == fa) continue;
        dfs(v, u);
    }
}
void solve() {
	int n; scanf("%d", &n);
    edge.clear();
    fori(1, n) g[i].clear();
    fori(1, n) scanf("%d%d%d", x+i, y+i, arr+i);
    double res = prim(n);

    double ans = 0;
    for (auto pi: edge) {
        int u, v;
        dfs(pi.first, pi.second);
        u = ma; ma = 0;
        dfs(pi.second, pi.first);
        v = ma; ma = 0;
        ans = max(ans, 1.0*(arr[u]+arr[v])/(res-getdis(pi.first, pi.second)));
    }
    cout << ans << endl;
}

signed main() {
	IOS;
	int t; scanf("%d", &t);
    cout << fixed << setprecision(2);
	while (t--) {
		solve();
	}
	return 0;
}

HDU - 2874 Connections between cities

题目大意

给你一个 n n n个节点 m m m条边的森林和 q q q次询问,每次询问给出两个点 u u u v v v,判断两个点是否连通,如果连通则输出其最短距离。

数据范围: 2 ≤ n ≤ 1 0 4 2 \le n \le 10^4 2n104 1 ≤ m ≤ n − 1 1 \le m \le n-1 1mn1 1 ≤ q ≤ 1 0 6 1 \le q \le 10^6 1q106

解题思路

非常经典的求LCA的题目,只是需要判断是否连通即可。这个也好判断,如果两个点的LCA为0,那么就不连通。

时间复杂度 O ( n log ⁡ n + q log ⁡ n ) O(n\log n +q\log n) O(nlogn+qlogn)

AC代码

#include <bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define pb push_back
#define endl '\n'

#define Yes cout << "Yes\n"
#define No cout << "No\n"
#define YES cout << "YES\n"
#define NO cout << "NO\n"
#define ff first
#define ss second
#define fori(x,y) for(int i=x;i<=(int)(y);++i)
#define forj(x,y) for(int j=x;j<=(int)(y);++j)
#define fork(x,y) for(int k=x;k<=(int)(y);++k)

#define debug(x) cout << #x << " = " << x << endl

typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;

ll MOD = 998244353;
ll qpow(ll a,ll p) {ll res=1; while(p) {if (p&1) {res=res*a%MOD;} a=a*a%MOD; p>>=1;} return res;}

const int N = 1e4+7;
unordered_map<ll, ll> g[N];

bool vis[N];
int dep[N], father[N];

int go[N][20];
ll dis[N][20];

void dfs(int u, int fa) {
    if (vis[u]) return;
    vis[u] = true;
    father[u] = fa;
    dep[u] = dep[fa]+1;
    for (auto pi: g[u]) {
        if (pi.first == fa) continue;
        dfs(pi.first, u);
    }
}

ll getdis(int u, int v) {
    ll ans = 0;
    if (dep[u]<dep[v]) swap(u, v);
    for (int j=19;j>=0;--j) {
        if (dep[go[u][j]]>=dep[v]) {
            ans += dis[u][j];
            u = go[u][j];
        }
    }
    if (u == v) return ans;
    for (int j=19;j>=0;--j) {
        if (go[u][j] != go[v][j]) {
            ans += dis[u][j]+dis[v][j];
            u = go[u][j], v = go[v][j];
        }
    }
    ans += dis[u][0]+dis[v][0];
    if (go[u][0] == 0) return -1;
    return ans;
}

signed main() {
	IOS;
    int n, m, q;
    while (~scanf("%d%d%d", &n, &m, &q)) {
        fori(1, n) {
            vis[i] = false;
            g[i].clear();
        }
        while (m--) {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            g[u].insert({v, w});
            g[v].insert({u, w});
        }
        fori(1, n) if (!vis[i]) dfs(i, 0);

        // void init
        fori(1, n) {
            go[i][0] = father[i];
            dis[i][0] = g[i][father[i]];
        }
        // fori(1, n) cout << father[i] << " ";
        // cout << endl;

        forj(1, 19) {
            fori(1, n) {
                go[i][j] = go[go[i][j-1]][j-1];
                dis[i][j] = dis[i][j-1]+dis[go[i][j-1]][j-1];
            }
        }

        while (q--) {
            int u, v; scanf("%d%d", &u, &v);
            ll res = getdis(u, v);
            if (res == -1) cout << "Not connected" << endl;
            else cout << res << endl;
        }
    }
	return 0;
}

HDU - 1217 Arbitrage

题目大意

你现在有一组货币的汇率,判断是否可以套汇。套汇的概念详见题面。

数据范围: 1 ≤ n ≤ 30 1 \le n \le 30 1n30

解题思路

数据范围很小,可以想到floyd算法三层循环求传递闭包。但是在转移的过程中需要求的是边权的乘积而非和。

时间复杂度 O ( n 3 ) O(n^3) O(n3)

AC代码

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define pb push_back

#define Yes cout << "Yes\n"
#define No cout << "No\n"
#define YES cout << "YES\n"
#define NO cout << "NO\n"
#define ff first
#define ss second
#define fori(x,y) for(int i=x;i<=(int)(y);++i)
#define forj(x,y) for(int j=x;j<=(int)(y);++j)
#define fork(x,y) for(int k=x;k<=(int)(y);++k)

#define debug(x) cout << #x << " = " << x << endl

typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;

ll MOD = 998244353;
ll qpow(ll a,ll p) {ll res=1; while(p) {if (p&1) {res=res*a%MOD;} a=a*a%MOD; p>>=1;} return res;}

const int N = 35;

double val[N][N];
signed main() {
	IOS;
	int n;
    int t = 0;
    while (1) {
        cin >> n;
        if (n == 0) break;
        t++;
        unordered_map<string,int> maps;
        fori(1, n) {
            string str; cin >> str;
            maps[str] = i;
        }
        fori(1, n) forj(1, n) val[i][j] = 0;

        int m; cin >> m;
        fori(1, m) {
            string str1, str2; double x;
            cin >> str1 >> x >> str2;
            val[maps[str1]][maps[str2]] = max(val[maps[str1]][maps[str2]], x);
        }

        fork(1, n) fori(1, n) forj(1, n) if (val[i][k]>0&&val[k][j]>0) val[i][j] = max(val[i][j], val[i][k]*val[k][j]);

        bool can = false;
        cout << "Case " << t << ": ";
        fori(1, n) if (val[i][i]>1+1e-10) {
            can = true;
            break;
        }
        if (can) Yes;
        else No;
        
    }
	return 0;
}

HDU - 2224 The shortest path

题目大意

给你 n n n个二维平面上的点,每个点有一个坐标 x i x_i xi y i y_i yi,且保证若 i < j i<j i<j x i < x j x_i<x_j xi<xj,你需要从点1出发走到点 n n n,再从点 n n n走到点1,从1走到 n n n的期间你只能往编号大的点走,从 n n n走到1期间你只能往编号小的点走。来回的过程中必须经过每个点一次,不能重复经过某个点,也不能遗漏某个点。

数据范围: 2 ≤ n ≤ 200 2 \le n \le 200 2n200

解题思路

上网搜了搜,发现这道题目叫做双调旅行商问题。解决这道题的思路是dp。由于路径的对称性,我们把这道题转化为:从点1出发,沿两条完全不同的路径(即没有点重合)走到 n n n的最短路径之和。

d p i , j dp_{i,j} dpi,j表示从点1出发,第一条路径走到了点 i i i,第二条路径走到了点 j j j的最短路径长度。保证 i > j i>j i>j。显然,初始状态为 d p 2 , 1 = d i s ( 1 , 2 ) dp_{2, 1}=dis(1, 2) dp2,1=dis(1,2)。接下来让我们看如何转移:

如果 j < i − 1 j<i-1 j<i1,那么 d p i , j = d p i − 1 , j + d i s ( i , i − 1 ) dp_{i,j}=dp_{i-1, j}+dis(i, i-1) dpi,j=dpi1,j+dis(i,i1),因为 i − 1 i-1 i1走在前面,那么点 i − 1 i-1 i1的下一个点必然是点 i i i,不能留空。

如果 j = = i − 1 j == i-1 j==i1,那么这个时候就需要枚举了。枚举 1 ≤ k ≤ j − 1 1 \le k \le j-1 1kj1,那么 d p i , j = min ⁡ ( d p j , k + d i s ( i , k ) ) dp_{i,j}=\min (dp_{j,k}+dis(i,k)) dpi,j=min(dpj,k+dis(i,k))。为什么?因为当 j j j走到 i − 1 i-1 i1时, i i i只能从更远的 k < j k < j k<j转移过来。而 d p k , j dp_{k, j} dpk,j d p j , k dp_{j, k} dpj,k是等价的,因此直接从 d p j , k dp_{j,k} dpj,k转移过来就可以了。

最后得到的答案为 d p n , n − 1 + d i s ( n − 1 , n ) dp_{n, n-1}+dis(n-1, n) dpn,n1+dis(n1,n)

时间复杂度为 O ( n 2 ) O(n^2) O(n2)

AC代码

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define pb push_back

#define Yes cout << "Yes\n"
#define No cout << "No\n"
#define YES cout << "YES\n"
#define NO cout << "NO\n"
#define ff first
#define ss second
#define fori(x,y) for(int i=x;i<=(int)(y);++i)
#define forj(x,y) for(int j=x;j<=(int)(y);++j)
#define fork(x,y) for(int k=x;k<=(int)(y);++k)

#define debug(x) cout << #x << " = " << x << endl

typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;

ll MOD = 998244353;
ll qpow(ll a,ll p) {ll res=1; while(p) {if (p&1) {res=res*a%MOD;} a=a*a%MOD; p>>=1;} return res;}

const int N = 205;
pii pos[N];
double dis(int i, int j) {
    return sqrt((pos[i].first-pos[j].first)*(pos[i].first-pos[j].first)+(pos[i].second-pos[j].second)*(pos[i].second-pos[j].second));
}

double dp[N][N];
signed main() {
	IOS;
	int n;
    cout << fixed << setprecision(2);
    while (~scanf("%d", &n)) {
        fori(1, n) scanf("%d%d", &pos[i].first, &pos[i].second);
        
        dp[2][1] = dis(1, 2);
        fori(3, n) forj(1, i-1) {
            dp[i][j] = 1e20;
            if (j == i-1) {
                fork(1, j-1) {
                    dp[i][j] = min(dp[i][j], dp[j][k]+dis(k, i));
                }
            }
            else dp[i][j] = dp[i-1][j]+dis(i, i-1);
        }
        cout << (dp[n][n-1]+dis(n, n-1)) << endl;
    }
	return 0;
}

HDU - 1142 A Walk Through the Forest

题目大意

给你一张 n n n个点 m m m条边的无向图, 1 1 1为起点, 2 2 2为终点,从1号节点出发,当经过某个点 u u u时,如果存在一个点 v v v,使得 u , v u,v u,v相连, v v v到2的最短路径比 u u u到2的最短路径要短,那么就可以从 u u u走到 v v v。计算从1到2所有不同的路径数量。

数据范围: 2 ≤ n ≤ 1 0 3 2 \le n \le 10^3 2n103,边权 1 ≤ d ≤ 1 0 6 1 \le d \le 10^6 1d106

解题思路

这道题是一道简单而又巧妙的题目。首先我们需要知道对于两个相邻节点 u u u v v v,怎么判断 u u u是否能够走向 v v v。很简单,从终点2跑一次单源最短路,然后就可以判断了。

那么如何计数?这里我的方法是dfs+记忆化搜索。我从节点1出发,逐一递归其邻接点,并将其所有邻接点的答案求和,更新该节点的dp值,再下一次搜索的时候直接使用该节点的dp值即可。

时间复杂度 O ( m log ⁡ n + n ) O(m\log n+n) O(mlogn+n)

AC代码

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define pb push_back

#define Yes cout << "Yes\n"
#define No cout << "No\n"
#define YES cout << "YES\n"
#define NO cout << "NO\n"
#define ff first
#define ss second
#define fori(x,y) for(int i=x;i<=(int)(y);++i)
#define forj(x,y) for(int j=x;j<=(int)(y);++j)
#define fork(x,y) for(int k=x;k<=(int)(y);++k)

#define debug(x) cout << #x << " = " << x << endl

typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;

ll MOD = 998244353;
ll qpow(ll a,ll p) {ll res=1; while(p) {if (p&1) {res=res*a%MOD;} a=a*a%MOD; p>>=1;} return res;}

const int N = 1e3+7;

vector<pii> g[N];
int dist[N];
char vis[N];
void Dijkstra(int s, int n) {
	fori(1, n) dist[i] = -1, vis[i] = 0;
	dist[s] = 0;
	priority_queue<pii, vector<pii>, greater<pii>> pri;  // 优先队列
	pri.push({dist[s],s});
	while (pri.size()) {
		pii pi = pri.top();
		pri.pop();
		if (vis[pi.ss]) continue;
		vis[pi.ss] = 1;
		dist[pi.ss] = pi.ff;
		for (int i=0;i<g[pi.ss].size();++i) {
			pri.push({pi.ff+g[pi.ss][i].ss, g[pi.ss][i].ff});
		}
	}
}

int dp[N];
int dfs(int u) {
    if (dp[u]) return dp[u];
    if (u == 2) return 1;
    int res = 0;
    for (auto pi: g[u]) {
        if (dist[pi.first]>=dist[u]) continue;
        res += dfs(pi.first);
    }
    return (dp[u] = res);
}

signed main() {
	IOS;
	int n, m;
    while (~scanf("%d", &n)&&n) {
        scanf("%d", &m);
        fori(1, n) g[i].clear();
        while (m--) {
            int u,v,w;
            scanf("%d%d%d", &u, &v, &w);
            g[u].push_back({v, w});
            g[v].push_back({u, w});
        }
        Dijkstra(2, n);
        fori(1, n) dp[i] = 0;
        cout << dfs(1) << endl;
    }
	return 0;
}

HDU - 3639 Hawk-and-Chicken

题目大意

班里有 n n n个同学,全班要开一次班会选举班长,每个同学都可以“支持”其他同学,这种支持关系是具有传递性的,若同学A支持同学B,同学B支持同学C,那么同学A也会支持同学C。但自己不能支持自己。给定 m m m对支持关系,找到受到最多人支持的若干个同学,并输出这些同学的编号和支持他们的人数。

数据范围: 2 ≤ n ≤ 5 × 1 0 3 2 \le n \le 5\times 10^3 2n5×103 1 ≤ m ≤ 3 × 1 0 4 1 \le m \le 3\times 10^4 1m3×104

解题思路

看到这道题和样例,就可以想到:如果 k k k个同学互相支持,那么这 k k k个同学的支持数都为 k − 1 k-1 k1。这里可以考虑使用Tarjan求强连通分量,然后缩点,得到一张DAG。接下来关键步骤来了:如何计算出支持数最多的同学呢?也就是如何在DAG上找到前驱数量最多的点。我当时想了半天,但是没有什么好的思路,因为无法用拓扑排序求出不重复的支持数。但考虑到 n n n的范围较小,或许可以通过减小常数通过 O ( n 2 ) O(n^2) O(n2)的算法。

考虑一张DAG,前驱数量最多的点只可能是出度为0的点。但直接在这张图上搜答案显然不太现实,因此我们考虑在缩点时建反图,然后求出后继数量最多的点。但这里只能用暴力了,从每一个入度为0的点开始跑dfs。复杂度是 O ( n 2 ) O(n^2) O(n2),但常数 ≤ 1 4 \le \frac{1}{4} 41,可以通过此题。最后把答案记录下来输出即可。

AC代码

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define pb push_back

#define Yes cout << "Yes\n"
#define No cout << "No\n"
#define YES cout << "YES\n"
#define NO cout << "NO\n"
#define ff first
#define ss second
#define fori(x,y) for(int i=x;i<=(int)(y);++i)
#define forj(x,y) for(int j=x;j<=(int)(y);++j)
#define fork(x,y) for(int k=x;k<=(int)(y);++k)

#define debug(x) cout << #x << " = " << x << endl

typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;

ll MOD = 998244353;
ll qpow(ll a,ll p) {ll res=1; while(p) {if (p&1) {res=res*a%MOD;} a=a*a%MOD; p>>=1;} return res;}

const int N = 5e3+7;
vector<int> g[N];

int scc[N], low[N], num[N];  // scc代表强连通分量的序号,low代表它所连接的最小祖先的序号,num代表访问到它时的序号
int dfn, cnt;  // dfn为访问序号,cnt记录强连通分量的个数和编号
stack<int> st;  // 用于保存属于同一个强连通分量的点
void dfs(int u) {
    st.push(u);
    low[u] = num[u] = ++dfn;  // 初始化
    fori(0, g[u].size()-1) {
        int v = g[u][i];
        if (!num[v]) {  // 如果没有访问过
            dfs(v);
            low[u] = min(low[u], low[v]);  // 递归返回子节点的low
        }
        else if (!scc[v]) {  // 如果子节点v访问过且不属于某一个强连通分量(好像直接else不加判断也可以)
            low[u] = min(low[u], num[v]);  // 该点的序号
        }
    }
    if (low[u] == num[u]) {  // 栈底:scc祖先
        cnt++;  // 这是一个新的强连通分量
        while (1) {
            int v = st.top();
            st.pop();
            scc[v] = cnt;  // 记录编号
            if (v == u) break;  // 栈底为该强连通分量的祖先节点
        }
    }
}

int siz[N], dp[N], deg[N];
vector<int> gg[N];

set<int> tmp;
void dfs2(int u) {
    tmp.insert(u);
    for (auto v: gg[u]) {
        if (tmp.find(v) != tmp.end()) continue;
        dfs2(v);
    }
}
void solve(int tt) {
	int n, m;
    cin >> n >> m;
    dfn = cnt = 0;
    fori(1, n) {
        g[i].clear();
        gg[i].clear();
        scc[i] = low[i] = num[i] = 0;
        siz[i] = dp[i] = deg[i] = 0;
    }
    fori(1, m) {
        int u, v;
        cin >> u >> v;
        u++, v++;
        g[u].push_back(v);
    }
    fori(1, n) if (!num[i]) dfs(i);
    fori(1, n) {
        siz[scc[i]]++;
        for (auto v: g[i]) {
            if (scc[v] == scc[i]) continue;
            gg[scc[v]].push_back(scc[i]);  // inverse
            deg[scc[i]]++;
        }
    }

    fori(1, cnt) {
        if (deg[i]) continue;
        tmp.clear();
        dfs2(i);
        int res = 0;
        for (auto u: tmp) res += siz[u];
        dp[i] = res;
    }
    

    set<int> sets;
    int ma = 0;
    fori(1, cnt) {
        if (dp[i]>ma) {
            ma = dp[i];
            sets.clear();
            sets.insert(i);
        }
        else if (dp[i] == ma) {
            sets.insert(i);
        }
    }

    vector<int> ans;
    fori(1, n) if (sets.find(scc[i]) != sets.end()) ans.push_back(i);
    sort(ans.begin(), ans.end());
    cout << "Case " << tt << ": " << ma-1 << endl;
    for (auto u: ans) cout << u-1 << " ";
    cout << endl;
}

signed main() {
	IOS;
	int t;
	cin >> t;
	fori(1, t) {
		solve(i);
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值