11.14-11.29小结(二)

刚打完cf,迫不及待的来写了。前两天又不知道忙啥去了。这次总结一下莫队和带修莫队。

1.莫队

1.1问题引入

前置知识:双指针,分块。

这里用的洛谷的P3901。

题意:给定一个序列,然后是若干组询问。每组询问区间[l,r]的出现的数是否互不相同。

把题意翻译过来,就是区间内每个数出现的次数最多不超过1。我们考虑暴力的做法,因为数据范围是1e5,所以可以直接开一个数组cnt[i],表示i这个数当前出现的次数。移动左右指针来移动区间。但是这样的时间复杂度会很大,最坏的情况下会到O(nm),这样的复杂度肯定是很难接受的,我们考虑沿用分块的一些思想,我们将所有询问(和操作)都给离线下来,所以普通莫队实际上是个离线的算法。然后记录每个询问的左右端点,然后排序。如果左端点在同一个块中按右端点排序,否则按左端点排序。为什么要这样排序呢?我们不妨从复杂度的角度来考虑一下这个问题。考虑分成\sqrt{n}块,则每个块的大小也是\sqrt{n},如果左指针在块内移动,则单词最多移动\sqrt{n}的位置。如果左指针要跨过某个块,最多是\sqrt{n}个块,所以复杂度也是O(\sqrt{n})级别的。再来看右指针,由于右指针被左指针限制移动,它的移动只能像右移动。复杂度也是O(\sqrt{n})的,总复杂度是O(m\sqrt{n}

(感觉这里讲的不是特别清楚qwq,佬们轻喷啊)

莫队实际上就是这样的一个算法(实际上和数据结构的联系更加紧密),假如我们当前求得了f(l,r)区间内所维护的答案,我们能在O(1)的时间内求解f(l+1,r),f(l-1,r),f(l,r+1),f(r-1)的状态,则可以用莫队求解,下面我们来具体的看一看add与del函数一般干了什么事情。

接下来讲一下本题的add与del函数,这俩函数是根据题目而决定的。在本题中,我们需要维护区间内出现的元素的个数,如果当前cnt[a[x]]为0时增加,累加答案。如果当前cnt[a[x]]为1时减少,计数减少。

细节在代码中看把~

1.2例题代码(洛谷P3901)

#include<iostream>
#include<cmath>
#include<algorithm>
#define endl '\n'
using namespace std;
const int N=1e5+10;
int n,m,a[N],bsz,cnt[N],sum,ans[N],L[N],R[N];
struct query{
	int l,r,id;
	bool operator<(const query &t){
		if(t.l/bsz!=l/bsz)return l<t.l;
		else return r<t.r;
	}
}q[N];
void add(int x){
	if(cnt[a[x]]==0)sum++;
	cnt[a[x]]++;
}
void del(int x){
	if(cnt[a[x]]==1)sum--;
	cnt[a[x]]--;
}
int main(){
	cin>>n>>m;
	bsz=sqrt(n);
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=m;i++){
		cin>>q[i].l>>q[i].r;
		q[i].id=i;
	}
	sort(q+1,q+m+1);
	int l=1,r=0;
	for(int i=1;i<=m;i++){
		while(q[i].l<l)add(--l);
		while(q[i].r>r)add(++r);
		while(q[i].l>l)del(l++);
		while(q[i].r<r)del(r--);
		ans[q[i].id]=sum;
		L[q[i].id]=l;
		R[q[i].id]=r;
	}
	for(int i=1;i<=m;i++){
		if(ans[i]==R[i]-L[i]+1)cout<<"Yes"<<endl;
		else cout<<"No"<<endl;
	}
	return 0;
}

1.3洛谷P2709

莫队裸题。假如某个数当前的个数是x,当变为(x+1),则会增加(2x+1)。同理从x+1减少到x的时候,数量会减少2x+1。各位可以将完全平方展开自行理解(如果不会完全平方应该也不会来学莫队吧)(x+1)^{2}-x^{2}=2x+1

下面贴个代码

#include<iostream>
#include<map>
#include<algorithm>
#include<cmath>
#define ll long long 
#define endl '\n'
using namespace std;
const int N=5e4+10;
inline int read(){
	char ch = getchar();
	int x = 0, f = 1;
	while(ch < '0' || ch > '9'){
		if(ch == '-') f = -1;
		ch = getchar();
	}
	while('0' <= ch && ch <= '9'){
		x = x * 10 + ch - '0';
		ch = getchar();
	}
	return x * f;
}
 
inline void write(ll x){
	if (x < 0) x = ~x + 1, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
 
int mp[N];
int n,m,bsz,a[N],k;
ll sum,ans[N];
struct query{
	int l,r,id;
	bool operator<(const query &q ){
		if(l/bsz!=q.l/bsz)return l<q.l;
		else return r<q.r;
	}
}q[N];
void add(int x){
	sum+=(2*mp[a[x]]+1);
	mp[a[x]]++;
}
void del(int x){
	mp[a[x]]--;
	sum-=(2*mp[a[x]]+1);
}
int main(){
	n=read();m=read();
	k=read();
	bsz=sqrt(n);
	for(int i=1;i<=n;i++)a[i]=read();
	//m=read();
	for(int i=1;i<=m;i++){
		q[i].l=read();
		q[i].r=read();
		q[i].id=i;
	}
	sort(q+1,q+m+1);
	int l=1,r=0;
	for(int i=1;i<=m;i++){
		while(l>q[i].l)add(--l);
		while(r<q[i].r)add(++r);
		while(l<q[i].l)del(l++);
		while(r>q[i].r)del(r--);
		ans[q[i].id]=sum;
	}
	for(int i=1;i<=m;i++){
		write(ans[i]);
		puts("");
	}
	return 0;
}

1.4洛谷P1194

题意:给定一个区间。询问当前区间任取两个,恰好颜色相同的概率。要求用分数表达。

首先,假设区间长度是l,那么分母就是\binom{len}{2},而分子就是其他颜色可能相同的袜子的和,假设某一时刻,某一颜色的袜子是x只,当增加后,会变成x+1只,会增加\binom{x+1}{2}-\binom{x}{2}=\left ( x+1 \right )*\left ( x \right )/2-\left ( x \right )*(x-1)/2=x种可能,而从x+1只减少到x只也是如此,所以我们考虑记录下每种颜色的袜子当前的数量cnt[i],再记录一个sum值用于记录当前分子的可能性,注意特判下分子是0的时候的输出。记得约下分就可以快乐的AC了。

#include<iostream>
#include<algorithm>
#include<cmath>
#define ll long long
#define int long long  
using namespace std;
const int N=8e4+10;
ll n,m,a[N],bsz,sum,cnt[N];
ll ans1[N],ans2[N];//记录某种颜色的数量
ll gcd(ll x,ll y){
	return y==0?x:gcd(y,x%y);
}
struct query{
	int l,r,id;
	bool operator<(const query &q){
		if(l/bsz!=q.l/bsz)return l<q.l;//同一个块中按左端点排序
		//否则按右端点排序
		return r<q.r;
	}
}q[N];
void add(int x){
	sum+=cnt[x];
	cnt[x]++;
}
void minor(int x){
	cnt[x]--;
	sum-=cnt[x];
}
signed main(){
	cin>>n>>m;
	bsz=sqrt(n);
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=m;i++){
		int l,r;
		cin>>l>>r;
		q[i].l=l;
		q[i].r=r;
		q[i].id=i;//记录下左右区间 
	}
	//排序
	sort(q+1,q+m+1);
	int l=1,r=0;//初始为空区间
	for(int i=1;i<=m;i++){
		while(l>q[i].l)add(a[--l]);
		while(r<q[i].r)add(a[++r]);
		while(l<q[i].l)minor(a[l++]);
		while(r>q[i].r)minor(a[r--]);
		ans1[q[i].id]=sum;//分子 
		int len=r-l+1;
		ans2[q[i].id]=len*(len-1)/2;
	}
	for(int i=1;i<=m;i++){
		ll k=gcd(ans1[i],ans2[i]);
		if(k==0||ans1[i]==0){
			cout<<"0/1"<<endl;
			continue;
		}else {
			ans1[i]/=k;
			ans2[i]/=k;
			cout<<ans1[i]<<"/"<<ans2[i]<<endl;
		}
	}
	return 0;
}

1.5洛谷P5268

一个简单的询问,但这题可不简单啊。

题意:给定一个区间[l1,r1]和另一个区间[l2,r2]。计算。get的含义见题目。乘法不好处理,我们在莫队问题处理加法比较多。考虑类似前缀和的思想,把区间[l,r]拆分成[1,r],[1,l-1]。这样我们可以对公式进行如下处理。记g(r)=get(1,r,x)。

\sum\limits_{x=0}^\infty \text get(l_1,r_1,x)*get(l_2,r_2,x)\\ \\=(g(r_1)-g(l_1-1))*(g(r_2)-g(l_2-1))\\ \\=g(r_1)*g(r_2)-g(r1)*g(l_2-1)-g(l_1-1)*g(r_2)+g(l_1-1)*g(l_2-1)

至此我们将一个区间拆成了四个区间(实际上不是区间啦),假如只考虑其中一组g(x)*g(y)的情况,开两个数组,cnt1[i]与cnt2[i],分别记录两个数组各元素的数量。当cnt1[i]增加时,答案累加cnt2[i],因为:(x+1)*y-x*y=y。如果是减少的情况也是一样的,至此这道题已经解决了。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long 
using namespace std;
const int N=5e4+10;
int n,m,a[N],bsz;
int cnt1[N],cnt2[N];
int l=0,r=-1;
ll sum,ans[N<<2];
struct query{
	int l,r,id;
	bool operator<(const query &t)const{
		if(l/bsz!=t.l/bsz)return l<t.l;
		return r<t.r;//同一块内按左端点排序否则按右端点排序 
	}
}q[N<<2];//注意询问开4倍空间
void addl(int x){
	++cnt1[a[x]];
	sum+=cnt2[a[x]];
}
void dell(int x){
	--cnt1[a[x]];
	sum-=cnt2[a[x]];
}
void addr(int x){
	++cnt2[a[x]];
	sum+=cnt1[a[x]];
}
void delr(int x){
	--cnt2[a[x]];
	sum-=cnt1[a[x]];
}
int main(){
	cin>>n;
	bsz=sqrt(n);
	for(int i=1;i<=n;i++)cin>>a[i];
	cin>>m;
	for(int i=1;i<=m;i++){
		//cout<<i;
		int l1,r1,l2,r2;
		cin>>l1>>r1>>l2>>r2;//读入数据
		q[i]={r1,r2,i};
		q[i+m]={r1,l2-1,i+m};
		q[i+m*2]={r2,l1-1,i+2*m};
		q[i+m*3]={l1-1,l2-1,i+3*m};
	}
	//cout<<1;
	for(int i=1;i<=4*m;i++){
		if(q[i].l>q[i].r)swap(q[i].l,q[i].r);
	}
	sort(q+1,q+4*m+1);//排序
	//cout<<1;
	for(int i=1;i<=m*4;i++){
		while(l<q[i].l)addl(++l);
		while(r<q[i].r)addr(++r);
		while(l>q[i].l)dell(l--);
		while(r>q[i].r)delr(r--);
		ans[q[i].id]=sum;//记录答案 
	}
	for(int i=1;i<=m;i++){
		ll res=ans[i]-ans[i+m]-ans[i+2*m]+ans[i+3*m];
		cout<<res<<endl;
	}
	return 0; 
}

至此莫队水题就切完啦

2.带修莫队

2.1问题引入

现在我们要处理的序列不再是静止的了,它需要支持一系列的修改操作。

我们知道莫队是一个离线算法,对于修改操作,我们也可以离线下来,到用到的时间再改。

由于加了一维,我们需要修改我们的询问数组。

struct query{
	int id, l, r, t;//id:第几个询问 t:第几个版本
	int posl,posr;//记录l所在的块和r所在的块的位置
	bool operator<(const query sec)const{
		if(posl!=sec.posl)return l<sec.l;
		if(posr!=sec.posr)return r<sec.r;
		return t<sec.t;
	}
}q[N];

我们还需要加入两个数组pos记录当前版本修改的位置,nxt数组暂存当前版本需要修改的值,等需要用到的时候进行交换。

带修莫队的块大小是取n^{\frac{2}{3}}的,具体证明我看了很多遍没看懂,先记个结论吧,如果有大佬会的话可以教教本蒟蒻啊。

我们需要加一个work函数用来记录版本的移动操作。这里做的事情就是删掉原来的颜色,加入暂存的颜色,将原来的颜色暂存,可以通过代码更直观的感受。

void work(int x,int l,int r){//x是记录的版本号所对应修改的位置 
	if(l<=pos[x]&&pos[x]<=r){
		if(--cnt[a[pos[x]]]==0)sum--;//删掉原版本的信息
		if(++cnt[nxt[x]]==1)sum++;//插入新版本的信息
	}
	swap(nxt[x],a[pos[x]]);//将原来版本的信息先暂存起来
}

接下来看完整代码

2.2例题代码(洛谷1903)

#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=133343;
const int M=1e6+10;
int n,m,a[N],bsz,sum,cnt[M],ans[N];
int pos[N],nxt[N],cnt1,cnt2;
inline int read(){
	char ch = getchar();
	int x = 0, f = 1;
	while(ch < '0' || ch > '9'){
		if(ch == '-') f = -1;
		ch = getchar();
	}
	while('0' <= ch && ch <= '9'){
		x = x * 10 + ch - '0';
		ch = getchar();
	}
	return x * f;
}
 
inline void write(int x){
	if (x < 0) x = ~x + 1, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
 
struct query{
	int id, l, r, t;
	int posl,posr;
	bool operator<(const query sec)const{
		if(posl!=sec.posl)return l<sec.l;
		if(posr!=sec.posr)return r<sec.r;
		return t<sec.t;
	}
}q[N];
void add(int x){
	if(cnt[a[x]]++==0)sum++;
}
void del(int x){
	if(--cnt[a[x]]==0)sum--;
}
void work(int x,int l,int r){//x是记录的版本号所对应修改的位置 
	if(l<=pos[x]&&pos[x]<=r){
		if(--cnt[a[pos[x]]]==0)sum--;
		if(++cnt[nxt[x]]==1)sum++;
	}
	swap(nxt[x],a[pos[x]]);
}
int main(){
	n=read();
	m=read();
	for(int i=1;i<=n;i++)cin>>a[i];
	bsz=pow(n,0.666);
	for(int i=1;i<=m;i++){
		char op;
		cin>>op;
		if(op=='Q'){
			int l,r;
			l=read();
			r=read();
			q[++cnt1]={cnt1,l,r,cnt2};//因为一次修改增加一个版本
			q[cnt1].posl=l/bsz;
			q[cnt1].posr=r/bsz; 
		}else{
			int x,k;
			x=read();
			k=read();
			pos[++cnt2]=x;
			nxt[cnt2]=k;//暂存当前版本信息 
		}
	}
	sort(q+1,q+cnt1+1);
	int l=1,r=0,t=-1;
	for(int i=1;i<=cnt1;i++){
		while(l>q[i].l)add(--l);
		while(r<q[i].r)add(++r);
		while(l<q[i].l)del(l++);
		while(r>q[i].r)del(r--);
		while(t<q[i].t)work(++t,l,r);
		while(t>q[i].t)work(t--,l,r);
		ans[q[i].id]=sum;
		//cout<<sum<<endl;
	}
	for(int i=1;i<=cnt1;i++){
		write(ans[i]);
		puts("");
	}
	return 0;
}

3.杂题

3.1CodeTon round 7 D

原题链接

这题是一个思维题,但是我不喜欢动脑子,所以我决定用暴力的方法写。

本题我不是赛时出的,补题写的。

题意:给定一个序列,序列中每个数只能是1或者2。有两种操作,有修改,有查询。查询的是是否存在一个区间[l,r]使得区间[l,r]的元素和为k。

一读题是不是很有线段树的味道了?没错!但是我的线段树一直T,调不来,我就把一颗线段树换成了树状数组。第一颗线段树维护原数组,没啥好说的,第二颗线段树维护当前区间是否存在1。关键是如何查询呢?我们考虑二分答案。如果当前维护的区间恰好为k,则找到了这样的区间。如果为k+1,我们查询[1,n-mid+1]是否有1,如果有,则可以通过移动区间把这个1去掉。至于为什么是n-mid+1,我们只能往后移动n-mid+1位了嘛。或者查询[mid,n]是否存在1,如果有1,我们可以移动区间加上这个1,左边删掉若干个2即可。线段树上套二分的复杂度(nlog^{​{_2{}}^{}}n),是可以卡过去的。

#include<iostream>
using namespace std;
const int N=2e6+10;
int n,m,a[N],b[N],T;//b用来记录某个数是不是1或0
int t[N];
int sumv[N<<2];
int lowbit(int x){
	return x&-x;
}
void add(int x,int k){
	for(int i=x;i<=n;i+=lowbit(i))t[i]+=k;
}
int ask(int x){
	int res=0;
	for(int i=x;i;i-=lowbit(i))res+=t[i];
	return res;
}
void pushup(int id){
	sumv[id]=sumv[id<<1]+sumv[id<<1|1];
}
void build(int id,int l,int r){
	if(l==r){
		sumv[id]=b[l];
		return;
	}
	int mid=l+r>>1;
	build(id<<1,l,mid);
	build(id<<1|1,mid+1,r);
	pushup(id);
}
void modify(int id,int l,int r,int x,int k){
	if(l==r){
		sumv[id]=k;
		return;
	}
	int mid=l+r>>1;
	if(x<=mid)modify(id<<1,l,mid,x,k);
	else modify(id<<1|1,mid+1,r,x,k);
	pushup(id);
}
int query(int id,int l,int r,int x,int y){
	if(x<=l&&y>=r)return sumv[id];
	int ans=0,mid=l+r>>1;
	if(x<=mid)ans+=query(id<<1,l,mid,x,y);
	if(y>mid)ans+=query(id<<1|1,mid+1,r,x,y);
	return ans;
}
int cal(int l,int r,int s){
	while(l<=r){
		int mid=l+r>>1;
		int check=ask(mid);//访问当前下标的元素
		if(check<s)l=mid+1;
		else if(check>s+1)r=mid-1;
		else if(check==s)return true;
		else if(check==s+1){
			int lfind=query(1,1,n,1,n-mid+1);
			int rfind=query(1,1,n,mid,n);
			if(lfind>0||rfind>0)return true;
			return false;
		}
		//cout<<l<<' '<<r<<endl;
	}
	return false;
}
void solve(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)t[i]=0;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		b[i]=a[i]%2;
		add(i,a[i]);
	}
	build(1,1,n);//建树
	while(m--){
		int op;
		cin>>op;
		if(op==1){
			int s;
			cin>>s;
			if(cal(1,n,s))cout<<"Yes"<<endl;
			else cout<<"No"<<endl;
		}else{
			int x,k;
			cin>>x>>k;
			add(x,k-a[x]);
			a[x]=k;
			modify(1,1,n,x,k%2);
		}
	}
}
int main(){
	cin>>T;
	while(T--){
		solve();
	}
	return 0;
}

3.2洛谷P4135

这题和总结(上)中的蒲公英很类似。

题意:一个序列,若干组询问,每次询问区间内出现的数的数量是偶数的数量,强制在线。

看见强制在线不用担心,分块是强制在线的。

下面我将从建块,修改,查询操作分别讲起。本题没有修改操作

建块:多开两个数组,cnt[i][j]表示从第i个块开始数字j出现的次数,实际上是一个类似与后缀和一样的东西,因为我们肯定是难以先求出每个块的元素再累加的,复杂度太高了。f[i][j]表示从第i个块到第j个块中数字出现次数是偶数的个数。

查询:如果区间[l,r]在同一块或者在相邻块中,我们只需要暴力枚举,如果当前数的出现次数变成偶数了,计数增加。如果当前出现次数变回大于1的奇数了,计数减少。

否则,答案初始化为f[id[l]+1][id[r]-1]。然后扫两边的元素,计数增加或减少,记得加上中间的元素出现的次数,这时候就得用到后缀和了。第i块到第j块出现k的次数实际上是cnt[id[l]+1][k]-cnt[id[r]][k],其他的处理就和之前在同一块一样了。

写完啦!

#include<iostream>
#include<cmath>
using namespace std;
const int N=1e5+10;
const int SQ=345;
int n,m,c,a[N],sq;
int ed[SQ],st[SQ],id[N],cnt[SQ][N],f[SQ][SQ],ans;//cnt表示某一块某个数字的个数
int t[N];//用来存放可能的数据
void build(){
	sq=sqrt(n);
	for(int i=1;i<=sq;i++){
		st[i]=n/sq*(i-1)+1;
		ed[i]=n/sq*i;
	}
	ed[sq]=n;
	for(int i=1;i<=sq;i++){
		for(int j=st[i];j<=ed[i];j++){
			id[j]=i;
		}
	}
	for(int i=1;i<=sq;i++){
		int s=0;
		for(int j=st[i];j<=n;j++){
			++cnt[i][a[j]];//空间换时间 
			if(cnt[i][a[j]]%2==0)++s;
			else if(cnt[i][a[j]]>=3)s--;//cnt是从第i块开始到n的数量 实际上应该是后缀和?3
			if(id[j]!=id[j+1])f[i][id[j]]=s;
			//预处理从x~y块的情况 
		}
	}
}
int query(int l,int r){
	int res=0;
	if(id[l]==id[r]||id[r]==id[l]+1){//暴力扫 
		//暴力扫一遍
		for(int i=l;i<=r;i++){
			t[a[i]]++;
			if(t[a[i]]%2==0)res++;
			else if(t[a[i]]>=3)res--;
		}
		for(int i=l;i<=r;i++){
			t[a[i]]--;
		}
	}else{
		res=f[id[l]+1][id[r]-1];//初始化res 取中间的数 
		for(int i=l;i<=ed[id[l]];i++){
			t[a[i]]++;
			//从id[l]+1~id[r]-1 
			int temp=cnt[id[l]+1][a[i]]-cnt[id[r]][a[i]];//记录中间某个数出现的次数 
			if((temp+t[a[i]])%2==0)res++;
			else if(temp+t[a[i]]>=3)res--;
		}
		for(int i=st[id[r]];i<=r;i++){
			t[a[i]]++;
			int temp=cnt[id[l]+1][a[i]]-cnt[id[r]][a[i]];//记录中间某个数出现的次数 
			if((temp+t[a[i]])%2==0)res++;
			else if(temp+t[a[i]]>=3)res--;
		}
		for(int i=l;i<=ed[id[l]];i++)t[a[i]]--;
		for(int i=st[id[r]];i<=r;i++)t[a[i]]--;//清空桶 
	}
	return res;
}
void check(){
//	for(int i=1;i<=sq;i++){
//		for(int j=1;j<=i;j++){
//			cout<<j<<' '<<i<<' '<<f[j][i]<<endl;
//		}
//	}
	cout<<"后缀和\n";
	for(int i=1;i<=sq;i++){
		for(int j=1;j<=c;j++){
			cout<<i<<' '<<j<<' '<<cnt[i][j]<<endl;
		}
	}
}
int main(){
	cin>>n>>c>>m;
	for(int i=1;i<=n;i++)cin>>a[i];
	build();
	//check();
	for(int i=1;i<=m;i++){
		int l,r;
		cin>>l>>r;
		l=((l+ans)%n)+1;
		r=((r+ans)%n)+1;
		if(l>r)swap(l,r); 
		//cout<<l<<' '<<r<<endl;
		ans=query(l,r);
		cout<<ans<<endl;
	}
	return 0;
}

今天就写到这里吧,明天还得去补CF的D和E题

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值