NOI2013 小Q的修炼 题解

IOI/NOI/CTSC/APIO等题解 专栏收录该内容
6 篇文章 0 订阅

感觉这种题答都没有题解,而我又想在这里保存一下代码,就写一份题解咯。


【题意】


一共有M个变量。

有一系列的操作,按顺序标为1~N。操作分为三种:

①普通操作:将一个变量加上一个量。

②条件跳转:给出两个量A、B和两个编号P、Q。

如果A<B那么跳到编号为P的操作,否则跳到编号为Q的操作。

③选择跳转:给出两个编号P、Q。在P和Q中任选一个跳过去。

注意以上的“量”可以是常量,也可以是目前某一个变量的值。

如果什么时候跳转到的编号不在1~N之间,则视为退出游戏。

你要给出选择跳转的决策,使退出游戏的时候第一个变量的值尽量的大。

(数据隐藏条件:跳转满足拓扑序)



【Case1和Case2】

 

C1的决策只有20个,直接DFS一下。

(顺便要写一个比较DJ的读入和模拟器)

发现C2跑了几秒也跑出来了QAQ。

#include<bits/stdc++.h>
#define N 1005
#define x first
#define y second
#define mk make_pair
using namespace std;
typedef pair<bool,int> Pair;
int P[N],Q[N],sign[N],ch[N],ret[N];
Pair A[N],B[N];char str[N][3];
int n,m,ans;
Pair read(){
  char tmp[10];int d;
  scanf("%s%d",tmp,&d);
  if (tmp[0]!='c') fprintf(stderr,"%s %d\n",tmp,d);
  if (tmp[0]=='c') return mk(0,d);
  return mk(1,d);
}
int Read(){
  char tmp[10];
  scanf("%s",tmp);
  return tmp[0]=='+'?1:-1;
}
void DFS(int k,vector<int>now){
  //fprintf(stderr,"%d\n",k);
  for (;k<=n&&str[k][0]!='s';){
    if (str[k][0]=='v') now[A[k].y]+=(B[k].x?now[B[k].y]:B[k].y)*sign[k],k++;
    if (str[k][0]=='i'){
      int t1=A[k].x?now[A[k].y]:A[k].y;
      int t2=B[k].x?now[B[k].y]:B[k].y;
      if (t1<t2) k=P[k];else k=Q[k];
    }
  }
  if (k>n){
    if (now[1]>ans){
      freopen("train2.out","w",stdout);
      fprintf(stderr,"%d\n",ans);
      for (int i=1;i<=*ch;i++)
        printf("%d ",ch[i]);
      ans=now[1];
    }
  }else{
    ch[++*ch]=1;DFS(P[k],now);--*ch;
    ch[++*ch]=2;DFS(Q[k],now);--*ch;
  }
}
int main(){
  freopen("train2.in","r",stdin);
  freopen("train2.out","w",stdout);
  scanf("%d%d",&n,&m);
  for (int i=1;i<=n;i++){
    
    scanf("%s",str[i]);int t;
    if (str[i][0]=='v') scanf("%d",&t),A[i]=mk(1,t),sign[i]=Read(),B[i]=read();
    if (str[i][0]=='i') A[i]=read(),B[i]=read(),scanf("%d%d",&P[i],&Q[i]);
    if (str[i][0]=='s') scanf("%d%d",&P[i],&Q[i]);
    fprintf(stderr,"%d %s\n",i,str[i]);
  }
  fprintf(stderr,"%s\n",str[n]);
  vector<int>now;
  for (int i=0;i<=m+2;i++) 
    now.push_back(i);
  DFS(1,now);
  
}


【Case3】

 

观察数据,发现可以分成一些互不干扰的块。

每一个块里有10个决策,每个决策会增加或减少编号为3~12的变量。

然后块的结尾会依据这3~12的变量给最终要求的1变量增加或减少值。

为什么称为“互不干扰”呢?因为每一块结尾都会把3~12的变量都清空。

然后只要对每一个块分别爆搜,把答案合并起来即可。

//#include<bits/stdc++.h>
#include<stdio.h>
#include<algorithm>
#include<cstring>
#include<vector>
#include "Windows.h"
#define N 350005
#define x first
#define y second
#define mk make_pair
using namespace std;
typedef pair<bool,int> Pair;
int P[N],Q[N],sign[N],ch[66],ret[66],now[66];
Pair A[N],B[N];char str[N][3];
int n,m,ans,best,st;
Pair read(){
  char tmp[10];int d;
  scanf("%s%d",tmp,&d);
  if (tmp[0]=='c') return mk(0,d);
  return mk(1,d);
}
int Read(){
  char tmp[10];scanf("%s",tmp);
  return tmp[0]=='+'?1:-1;
}
void DFS(int x){
  //fprintf(stderr,"%d ",x);
  if (x>10){
    int get=0,b=10*12+st;
    for (int i=b;i<b+170;i++){
      //fprintf(stderr,"%d\n",i);
      if (str[i][0]=='i'){
        int t1=A[i].x?now[A[i].y]:A[i].y;
        int t2=B[i].x?now[B[i].y]:B[i].y;
        if (t1<t2) i=P[i]-1;else i=Q[i]-1;
      }else if (A[i].y==1)
        get+=sign[i]*(B[i].x?now[B[i].y]:B[i].y);
    }
    if (get>best) 
      best=get,memcpy(ret,ch,sizeof(ch));
    return;
  }
  ch[x]=2;DFS(x+1);
  int b=st+(x-1)*12;
  for (int i=1;i<=10;i++)
    now[i+2]+=B[b+i].y;
  ch[x]=1;DFS(x+1);
  for (int i=1;i<=10;i++)
    now[i+2]-=B[b+i].y;
}
int main(){
  freopen("train3.in","r",stdin);
  freopen("train3.out","w",stdout);
  scanf("%d%d",&n,&m);
  for (int i=1;i<=n;i++){
    scanf("%s",str[i]);int t;
    if (str[i][0]=='v') scanf("%d",&t),A[i]=mk(1,t),sign[i]=Read(),B[i]=read();
    if (str[i][0]=='i') A[i]=read(),B[i]=read(),scanf("%d%d",&P[i],&Q[i]);
    if (str[i][0]=='s') scanf("%d%d",&P[i],&Q[i]);
  }fprintf(stderr,"Start\n");
  for (st=1;st<=n;st+=170){
    best=0;
    for (int i=3;i<=12;i++) now[i]=0;
    DFS(1);
    ans+=best;fprintf(stderr,"%d %d\n",st,best);
    for (int k=1;k<=10;k++)
      printf("%d ",ret[k]);
  }fprintf(stderr,"%d\n",ans);
  return 0;
}


【Case4~6】

 

发现只有两个变量。

2变量的增减都比较小,而且所有的条件跳转都只和2变量有关。

这就可以想到按顺序DP下去,每次记录当前2变量的值是多少即可。

最后再回去输出方案。说起来比较轻松,实际还是有很多细节的。

为了方便,DP的意义可以从“做完第i个操作,2变量的值为k”改成“做第i个操作之前,2变量的值为k”这样。

写完之后大概改一下也可以过第5个点。

//#include<bits/stdc++.h>
#include<stdio.h>
#include<algorithm>
#include<cstring>
#include<vector>
#define N 10005
#define x first
#define y second
#define mk make_pair
using namespace std;
typedef pair<bool,int> Pair;
int P[N],Q[N],sign[N];
Pair A[N],B[N];char str[N][3];
struct data{
  int v,prei,prek,ch;
  void Min(int _v,int _ch,int _prei,int _prek){
    if (_v<=v) return;
    v=_v;ch=_ch;prei=_prei;prek=_prek;
  }
  int operator < (const data &b)const{return v<b.v;}
}F[6005][N],ans;int n,m;
Pair read(){
  char tmp[10];int d;
  scanf("%s%d",tmp,&d);
  if (tmp[0]=='c') return mk(0,d);
  return mk(1,d);
}
int Read(){
  char tmp[10];
  scanf("%s",tmp);
  return tmp[0]=='+'?1:-1;
}
int Case=0;
void DFS(int i,int k){
  //fprintf(stderr,"%d %d\n",i,k);
  //if ((++Case)==20) return;
  if (i<=2) return;
  DFS(F[i][k].prei,F[i][k].prek);
  if (F[i][k].ch>-1) printf("%d ",F[i][k].ch);
}
int main(){
  freopen("train5.in","r",stdin);
  freopen("train5.out","w",stdout);
  scanf("%d%d",&n,&m);
  for (int i=1;i<=n;i++){
    scanf("%s",str[i]);int t;
    if (str[i][0]=='v') scanf("%d",&t),A[i]=mk(1,t),sign[i]=Read(),B[i]=read();
    if (str[i][0]=='i') A[i]=read(),B[i]=read(),scanf("%d%d",&P[i],&Q[i]);
    if (str[i][0]=='s') scanf("%d%d",&P[i],&Q[i]);
  }
  //F[i][k] 做第i行之前,v2的值为k的最大的v1的值
  //for (int i=0;i<=n+1;i++) F[i]=f[i]+N/2;int bug=0;
  for (int i=0;i<=n+1;i++) for (int k=0;k<N;k++) F[i][k].v=-1e9;
  F[2][5000].v=0;ans.v=-1e9;
  for (int i=2;i<=n;i++)
    for (int k=0;k<N;k++) 
      if (F[i][k].v>-1e9){
        //printf("%d %d %d\n",i,k,F[i][k].v);
        if (str[i][0]=='v'){
          int newk=k,newv=F[i][k].v;
          if (A[i].x&&A[i].y==2)
            newk+=sign[i]*B[i].y;
          else if (A[i].x&&A[i].y==1)
            newv+=sign[i]*B[i].y;
          //if (i==6)
            //fprintf(stderr,"%d %d\n",newv,newk);
          F[i+1][newk].Min(newv,-1,i,k);
        }else if (str[i][0]=='i'){
          //if (bug) fprintf(stderr,"%d %d\n%d %d\n",A[i].x,A[i].y,B[i].x,B[i].y);
          int t1=A[i].x?k:A[i].y;
          int t2=B[i].x?k:B[i].y,newi;
          //if (bug) fprintf(stderr,"%d %d\n",t1,t2);
          if (t1<t2) newi=P[i];else newi=Q[i];
          //if (bug) fprintf(stderr,"Newi:%d\n",newi);
          if (i==8)
            fprintf(stderr,"%d %d %d %d\n",t1,t2,newi,F[i][k].v);
          if (newi>=1&&newi<=n)
            F[newi][k].Min(F[i][k].v,-1,i,k);
          else ans=max(ans,(data){F[i][k].v,i,k});
        }else {
          if (P[i]>=1&&P[i]<=n)
            F[P[i]][k].Min(F[i][k].v,1,i,k);
          else ans=max(ans,(data){F[i][k].v,i,k});
          if (Q[i]>=1&&Q[i]<=n)
            F[Q[i]][k].Min(F[i][k].v,2,i,k);
          else ans=max(ans,(data){F[i][k].v,i,k});
        }
      }
  fprintf(stderr,"Type1Ans:%d %d\n",ans.v,n);
  for (int k=0;k<N;k++)
    ans=max(ans,(data){F[n+1][k].v,n+1,k});
  fprintf(stderr,"Type2Ans:%d\n",ans.v);
  DFS(ans.prei,ans.prek);
}



【Case7~8】

 

首先和之前的一个点差不多,发现这些操作可以分成几组,每组格式类似。

发现每组的开头有些和2变量有关的判断:若2的值怎么怎么样强制你跳到后面一块。

否则的话,你也有对这一块的决策权:即你仍然有机会能跳出这一块。

如果你跳进这一块的话,可以像之前爆搜的那样搜出一些变量的取值。

在最外面再套一个DP,记录变量2的状态、决策是否进去某一块啥的。

找方案的时候可能更加麻烦。

//#include<bits/stdc++.h>
#include<stdio.h>
#include<algorithm>
#include<cstring>
#include<vector>
#include "Windows.h"
#define N 35005
#define M 1005
#define x first
#define y second
#define mk make_pair
using namespace std;
typedef pair<bool,int> Pair;
int ret[205][21],best[205];
int P[N],Q[N],sign[N],ch[21],now[66];
Pair A[N],B[N];char str[N][3];
int F[N][M],pre[N][M],walk[N][M],extra[N][M];
int n,m,ans,st,i,k,newk,tmp,tot;
Pair read(){
  char tmp[10];int d;
  scanf("%s%d",tmp,&d);
  if (tmp[0]=='c') return mk(0,d);
  return mk(1,d);
}
int Read(){
  char tmp[10];scanf("%s",tmp);
  return tmp[0]=='+'?1:-1;
}
void DFS(int x){
  //fprintf(stderr,"%d ",x);
  if (x>10){
    int get=0,b=st+12*10;
    for (int i=b;i<st+175;i++){
      //fprintf(stderr,"%d\n",i);
      if (str[i][0]=='i'){
        int t1=A[i].x?now[A[i].y]:A[i].y;
        int t2=B[i].x?now[B[i].y]:B[i].y;
        if (t1<t2) i=P[i]-1;else i=Q[i]-1;
      }else if (A[i].y==1)
        get+=sign[i]*(B[i].x?now[B[i].y]:B[i].y);
    }
    if (get>best[tot]) 
      best[tot]=get,memcpy(ret[tot],ch,sizeof(ch));
    return;
  }
  ch[x]=2;DFS(x+1);
  int b=st+(x-1)*12;
  for (int i=1;i<=10;i++)
    now[i+2]+=B[b+i].y;
  ch[x]=1;DFS(x+1);
  for (int i=1;i<=10;i++)
    now[i+2]-=B[b+i].y;
}
void BACK(int i,int k){
  if (i<=1) return;
  fprintf(stderr,"%d %d %d %d %d\n",i,k,F[i][k],walk[i][k],extra[i][k]);
  BACK(i-1,pre[i][k]);
  if (walk[i][k]!=-1) 
    printf("%d ",walk[i][k]),assert(walk[i][k]>0);
  if (extra[i][k])
    for (int c=1;c<=10;c++)
      printf("%d ",ret[extra[i][k]][c]);
}
int main(){
  freopen("train8.in","r",stdin);
  freopen("train8.out","w",stdout);
  scanf("%d%d",&n,&m);
  for (i=1;i<=n;i++){
    scanf("%s",str[i]);int t;
    if (str[i][0]=='v') scanf("%d",&t),A[i]=mk(1,t),sign[i]=Read(),B[i]=read();
    if (str[i][0]=='i') A[i]=read(),B[i]=read(),scanf("%d%d",&P[i],&Q[i]);
    if (str[i][0]=='s') scanf("%d%d",&P[i],&Q[i]);
  }fprintf(stderr,"Start\n");
  for (st=6;st<=n;st+=175){
    best[++tot]=0;
    assert(str[st][0]=='s');
    for (i=3;i<=12;i++) now[i]=0;
    DFS(1);
    fprintf(stderr,"%d %d\n",st,best[tot]);
    for (k=1;k<=10;k++)
      assert(ret[tot][k]>0);
  }
  for (i=0;i<=tot+1;i++)
    for (k=0;k<M;k++) F[i][k]=-1e9;
  F[1][1000]=0;assert(tot==200);
  for (i=1;i<=tot;i++)
    for (k=0;k<M;k++)
      if (F[i][k]>-1e9){
        //fprintf(stderr,"%d %d %d\n",i,k,F[i][k]);
        st=1+(i-1)*175+1;
        assert(str[st][0]=='i'&&A[st].y==2);
        if (k<B[st].y){
          if (F[i][k]>F[i+1][k])
            F[i+1][k]=F[i][k],pre[i+1][k]=k,walk[i+1][k]=-1,extra[i+1][k]=0;
          continue;
        }st+=2;assert(str[st][0]=='s');
        if (F[i][k]>F[i+1][k])
          F[i+1][k]=F[i][k],pre[i+1][k]=k,walk[i+1][k]=2,extra[i+1][k]=0;
        ++st;assert(A[st].y==2&&B[st].x==0);
        //fprintf(stderr,"%d %d\n",sign[st],B[st].y);
        newk=k+sign[st]*B[st].y;
        if (newk>k) 
          fprintf(stderr,"%d %d\n",i,k);
        assert(newk<=k);
        //fprintf(stderr,"%d\n",F[i][k]+best[i]);
        if (F[i][k]+best[i]>F[i+1][newk])
          F[i+1][newk]=F[i][k]+best[i],pre[i+1][newk]=k,walk[i+1][newk]=1,extra[i+1][newk]=i;
      }
  fprintf(stderr,"%d %d\n",F[tot][500],F[tot+1][500]);
  for (k=0;k<M;k++)
    if (F[tot+1][k]>ans)
      ans=F[tot+1][k],tmp=k;
  fprintf(stderr,"%d\n",ans);
  BACK(tot+1,tmp);
  return 0;
}



【Case9~10】

 

这两个点感觉和之前的特别像。

但是我用之前的程序跑怎么也跑不过。后来发现了一些坑。

首先,循环节的长度稍微变化了一下。

然后假设循环节的长度为L,我发现操作数竟然不是L的倍数!

这是怎么回事呢?大概看一下,发现几千行的一个地方不是按照原来的规律在变化。

可以称这些地方为"噪点”。

如果再找出一行“噪点”,就可以大概归纳出其性质,然后把它们删掉即可。

对于删掉后的数据我还是搞了很长时间:显然我把每一条操作重新编号了。

因为在跳转的时候,编号还是会按照原先数据的编号,所以要搞一个比较麻烦的映射。

此外我记得还有很多细节,比如在块之间跳跃的时候,好像还可以往后跳好几块啥的,总之特别坑。

#include<bits/stdc++.h>
#include<stdio.h>
#include<algorithm>
#include<cstring>
#include<vector>
#include "Windows.h"
#define N 35005
#define M 1005
#define x first
#define y second
#define mk make_pair
using namespace std;
typedef pair<bool,int> Pair;
int ret[205][21],best[205],S[1005];
int P[N],Q[N],index[N],pos[N],sign[N],ch[21],now[66];
Pair A[N],B[N];char str[N][3];
int F[N][M],prei[N][M],prek[N][M],walk[N][M],extra[N][M];
int n,m,ans,st,i,k,newk,newi,tmpi,tmpk,tot,j;
Pair read(){
  char tmp[10];int d;
  scanf("%s%d",tmp,&d);
  if (tmp[0]=='c') return mk(0,d);
  return mk(1,d);
}
int Read(){
  char tmp[10];scanf("%s",tmp);
  return tmp[0]=='+'?1:-1;
}
int bug=0;
void DFS(int x){
  if (x>10){
    if (bug) 
			assert(str[st+12*10][0]=='i'&&A[st+12*10].y==3);
    int get=0,b=st+12*10;
    for (int i=b;i<st+174;i++){
      //fprintf(stderr,"%d\n",i);
      if (str[i][0]=='i'){
        int t1=A[i].x?now[A[i].y]:A[i].y;
        int t2=B[i].x?now[B[i].y]:B[i].y;
        if (t1<t2) i=P[i]-1;else i=Q[i]-1;
      }else if (A[i].y==1)
        get+=sign[i]*(B[i].x?now[B[i].y]:B[i].y);
    }//if (bug) fprintf(stderr,"%d\n",get);
    if (get>best[tot]) 
      best[tot]=get,memcpy(ret[tot],ch,sizeof(ch));
    return;
  }
  ch[x]=2;DFS(x+1);
  int b=st+(x-1)*12;
  for (int i=1;i<=10;i++)
    now[i+2]+=B[b+i].y;
  ch[x]=1;DFS(x+1);
  for (int i=1;i<=10;i++)
    now[i+2]-=B[b+i].y;
}
void BACK(int i,int k){
  if (i<=1) return;
  //fprintf(stderr,"%d %d %d %d %d\n",i,k,F[i][k],walk[i][k],extra[i][k]);
  BACK(prei[i][k],prek[i][k]);
  //fprintf(stderr,"%d\n",i);
  if (walk[i][k]!=-1) 
    printf("%d ",walk[i][k]),assert(walk[i][k]>0);
  if (extra[i][k])
    for (int c=1;c<=10;c++)
      printf("%d ",ret[extra[i][k]][c]);
}
int calc(int x){
	for (int i=1;i<=*S;i++)
		if (S[i]==x){
			int j=i;
			for (;j<*S&&S[j+1]==S[j]+1;++j);
			x=S[j]+1;break;
	  }
	x-=lower_bound(S+1,S+*S+1,x)-(S+1);
	return x;
}
int main(){
  freopen("train10.in","r",stdin);
  scanf("%d%d",&n,&m);n-=8;int last=0;
  for (i=1,j=1;i<=n;i++,j++){
    scanf("%s",str[i]);int t;index[i]=j;
    if (str[i][0]=='v') scanf("%d",&t),A[i]=mk(1,t),sign[i]=Read(),B[i]=read();
    if (str[i][0]=='i') A[i]=read(),B[i]=read(),scanf("%d%d",&P[i],&Q[i]);
    if (str[i][0]=='s') scanf("%d%d",&P[i],&Q[i]);
    if (i%174==2&&i>10) 
      if (!A[i].x) 
        assert(str[i][0]!='s'),i--,n--,S[++*S]=j;
  }printf("%d ",n);
  for (i=1;i<=n;i++)
  	P[i]=calc(P[i]),Q[i]=calc(Q[i]);
  for (st=6;st<=n;st+=174){
    best[++tot]=-1e9;
    assert(str[st][0]=='s');
    for (i=3;i<=12;i++) now[i]=0;
    DFS(1);
    //fprintf(stderr,"%d %d\n",st,best[tot]);
    for (k=1;k<=10;k++)
      assert(ret[tot][k]>0);//fprintf(stderr,"%d ",ret[tot][k]);
  }assert(tot==200);
  bug=0;tot=0;
  for (st=2;st<=n;st+=174) pos[st]=++tot;
  for (i=0;i<=tot+1;i++)
    for (k=0;k<M;k++) F[i][k]=-1e9;
  F[1][1000]=0;
  for (i=1;i<=tot;i++)
    for (k=0;k<M;k++)
      if (F[i][k]>-1e9){
        //fprintf(stderr,"%d %d %d\n",i,k,F[i][k]);
        st=1+(i-1)*174+1;
        assert(str[st][0]=='i'&&A[st].y==2);
        if (k<B[st].y){
        	if (P[st+1]>=1&&P[st+1]<=n){
            //printf("%d %d %d\n",st+1,index[P[st+1]],index[Q[st+1]]);
            newi=pos[P[st+1]];assert(newi);
            if (F[i][k]>F[newi][k])
              F[newi][k]=F[i][k],prei[newi][k]=i,prek[newi][k]=k,
              walk[newi][k]=-1,extra[newi][k]=0;
          }else 
            if (F[i][k]>F[tot+1][k])
              F[tot+1][k]=F[i][k],prei[tot+1][k]=i,prek[tot+1][k]=k,
              walk[tot+1][k]=-1,extra[tot+1][k]=0;
          continue;
        }st+=2;assert(str[st][0]=='s');
        if (Q[st]<1||Q[st]>n){
          if (F[i][k]>F[tot+1][k])
            F[tot+1][k]=F[i][k],prei[tot+1][k]=i,prek[tot+1][k]=k,
            walk[tot+1][k]=2,extra[tot+1][k]=0;
        }else{
          assert(pos[Q[st]]);
          newi=pos[Q[st]];
          if (F[i][k]>F[newi][k])
            F[newi][k]=F[i][k],prei[newi][k]=i,prek[newi][k]=k,
            walk[newi][k]=2,extra[newi][k]=0;
        }
        ++st;assert(A[st].y==2&&B[st].x==0);
        //fprintf(stderr,"%d %d\n",sign[st],B[st].y);
        newk=k+sign[st]*B[st].y;
        assert(newk<=k);
        //fprintf(stderr,"%d\n",F[i][k]+best[i]);
        if (F[i][k]+best[i]>F[i+1][newk])
          F[i+1][newk]=F[i][k]+best[i],prei[i+1][newk]=i,prek[i+1][newk]=k,
          walk[i+1][newk]=1,extra[i+1][newk]=i;
      }
  //fprintf(stderr,"%d %d\n",F[tot][500],F[tot+1][500]);
  for (k=0;k<M;k++)
    if (F[tot+1][k]>ans)
      ans=F[tot+1][k],tmpi=tot+1,tmpk=k;
  fprintf(stderr,"%d\n",ans);
  freopen("train10.out","w",stdout);
  BACK(tmpi,tmpk);
  return 0;
}

  • 2
    点赞
  • 1
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值