树状数组专题(附题解)

树状数组专题(附题解)

前言

关于不断修改查询的题目基本可以分为四类,有易到难分别为
· 单点修改,单点查询
· 单点修改,区间查询
· 区间修改,单点查询
· 区间修改,区间查询
第一个用数组就可以轻松解决(修改a[i],查询a[j] 都是O(1)的操作),而后三种情况就可以用树状数组来解决了。

树状数组简介

什么是树状数组

字面意思,就是用一个数组来模拟树。

为什么不用直接用树

建议一棵树太麻烦了,可能还需要用到结构体、指针什么的。能用树状数组解决的问题非要建树就是浪费时间

能解决的为题

大多数基于区间上的修改和查询问题

和线段树的比较

线段树能解决的问题树状数组不一定能解决,树状数组能解决的问题线段树一定能解决,但是树状数组实现起来更方便,代码更简洁,就像是用字符串模拟四则运算可以解决大数运算的问题,也可以解决1+1的问题,但是为什么不用字符串解决1+1呢?就是因为麻烦,基本所有语言都能直接计算1+1,没必要用大数运算。树状数组也是一样,能用树状数组解决的问题,就没必要用线段树。

树状数组和树的相似之处

我用两张图来比较一下
树
应该可以看得出来这是一棵树,其中a数组保存原始的数组,b数组保存这他左右子树的和,b[1] = a[1] + a[2] ,b[5] = b[1] + b[2] 之类的,现在我要树用数组来模拟出来,我们把这颗树压缩一下看一看
树状数组
最下层的是原来的a数组,我们看红圈中的c数组,这个c数组就叫做树状数组
c[1] = a[1]
c[2] = a[1] + a[2]
c[3] = a[3]
c[4] = a[1] + a[2] + a[3] + a[4]
c[5] = a[5]
c[6] = a[5] + a[6]
c[7] = a[7]
c[8] = a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8]
这样的话我们就把数组a以及数组的一些部分和保存在了一个数组c里
比如想得到a[4] = c[4] - c[3] - c[2],或者a[6] = c[6] - c[5]
那我们前n项的和也很方便
sum[5] = c[5] + c[4]
sum[8] = c[8]
那怎么知道sum[n] = ? 呢?观察c数组,我们把c[x] 中的x用二进制表示
c[0001] = a[1]
c[0010] = a[1] + a[2]
c[0011] = a[3]
c[0100] = a[1] + a[2] + a[3] + a[4]
c[0101] = a[5]
c[0110] = a[5] + a[6]
c[0111] = a[7]
c[1000] = a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8]
发现没有c[x] 中x末尾0的个数为y的话,就代表是c[x] 中储存的元素个数为2y个,并且是从a[x] 往前数2y
那求sum[7]的话就是从c[7]开始加c[7]=c[0111]存了20个,7-20 = 6,然后加c[6],c[6]为c[0110]存了21 2个,6-21=4,所以再加c[4],c[4]为c[0100],存了22个,再往前4-22 = 0结束了。
那该怎么求末尾零的个数呢?我们可以直接通过位运算求2y这个数,比如一个数010100100,我们对他取反就是101011011,再加1就是101011100,这样直接对两数进行&运算就可以了。010100100&101011100 = (100)2 = (4)10,而取反加一操作得到的正是原来数的相反数,也就是加负号,简化一下就是 x&-x,这个操作叫做lowbit;
用代码表示就是

int lowbit(x) { return x&-x; }
int getsum(int n)
{
	int sum = 0;
	while(n > 0)
	{
		sum += c[n];
		n -= lowbit(x);
	}
	return sum;
}

既然这么求和,那么存c数组的时候就能按照这个过程的逆过程来存
求比如a[1] 在c[1] c[2] c[4] c[8]里面均出现了,那么 但a[1]改变时c[1] c[2] c[4] c[8]都应该改变,还是用lowbit来操作,1+lowbit(1) = 2; 2+lowbit(2) = 4; 4+lowbit(4) = 8;代码为

void update(int i,int k) // 位置i增加了k,减少就是update(i,-k);
{
	while(i<=n)
	{	
		c[i] += k;
		i -= lowbit(i);
	}
}

完整的操作就是

int lowbit(x) { return x&-x; }
int getsum(int n)
{
	int sum = 0;
	while(n > 0)
	{
		sum += c[n];
		n -= lowbit(x);
	}
	return sum;
}
void update(int i,int k) // 位置i增加了k,减少就是update(i,-k);
{
	while(i<=n)
	{	
		c[i] += k;
		i -= lowbit(i);
	}
}
int main()
{
	//位置a增加k,求(a,b)的和
	update(a,k);
	int ans = getsum(b) - getsum(a-1);
]

这就是单点修改,区间查询。
下面来介绍区间修改,单点查询。这里就是多用了一个差分数组D[x] = a[x] - a[x-1]
这样的话区间修改就变成了两个端点的修改了。
比如
A = 1 2 3 4 5 6
D = 1 1 1 1 1 1
这样区间(1,2)加一的结果就是
A = 2 3 3 4 5 6
D = 2 1 0 4 5 6
发现没有,改变的就是端点1,和端点3(也就是2+1)
D保存的是A中相邻两项的差值,那D的前n项和就是A的第n项的值
D[1] = A[1]-A[0], D[2] = A[2] - A[1] , D[3] = A[3]-A[2]
D[3] + D[2] + D[1] = A[3] - A[2] + A[2] - A[1] + A[1] - A[0] = A[3];
我们更新区间(a,b)让他们都加10相当去数组D[a]+10, D[b+1]-10。我们只需要维护这个差值就好了,getsum(n)操纵得到的就是第n项的值。
区间修改,区间查询怎么解决的?
我们已经知道D数组前n项和就是A[n]的值,那我们从A[1] 一直加到A[n]不就有了A数组的前n项和了嘛,但这个操作需要O(n)的复杂度,我们试着在用一个树状数组来求A[1]到A[n]的和

∑ i = 1 n a i \sum_{i=1}^{n}a_i i=1nai = a[1] + a[2] + … + a[n]
= ∑ i = 1 1 d i \sum_{i=1}^{1}d_i i=11di + ∑ i = 1 2 d i \sum_{i=1}^{2}d_i i=12di + … + ∑ i = 1 n d i \sum_{i=1}^{n}d_i i=1ndi
= d[1] + (d[1] + d[2]) + … + (d[1] + d[2] + … + d[n])
=n * d[1] + (n-1) * d[2] + … + 1 * d[n]
=n * (d[1] + d[2] + … + d[n]) - (0 * d[1] + 1 * d[2] + 2 * d[3] + … + (n-2) * d[n-1] + (n-1) * d[n])
其中d[1] + d[2] + … + d[n]就是A[n] ,我们可以通过刚才那个树状数组得到 —> getsum(n);那我们现在只需要用另一个树状数组存一下(n-1) * d[n],这样我们就能快速求出 ∑ i = 1 n a i \sum_{i=1}^{n}a_i i=1nai的值了,当a[i]变化的时候 i 是固定不变的所以 (i-1) * d[n] 中只有d[n] 改变了,那a[i] + k 就相当于(i-1) * d[n] + (i-1)*k

void update(int i,long long k)  // (a,b)+k  =>  a + k && b+1 - k
{
    int index = i;
    while(index<=maxn)
    {
        c[index] += k;  //存 d[i]
        b[index] += (i-1)*k; // 存 (n-1) * d[n]
        index += lowbit(index);
    }
}
//n * (d[1] + d[2] + ... + d[n]) - (0 * d[1] + 1 * d[2] + 2 * d[3] + ... + (n-2) * d[n-1] + (n-1) * d[n]) 
long long getsum(int x)
{
    long long ans = 0;
    long long an = 0;
    int index = x;
    while(index > 0)
    {
        an += c[index];  // 求d[1] + d[2] + ... + d[n]
        ans += b[index];  // 求0 * d[1] + 1 * d[2] + 2 * d[3] + ... + (n-2) * d[n-1] + (n-1) * d[n]
        index -= lowbit(index);
    }    
    ans = x*an - ans; //n * (d[1] + d[2] + ... + d[n]) - (0 * d[1] + 1 * d[2] + 2 * d[3] + ... + (n-2) * d[n-1] + (n-1) * d[n]) 
    return ans;
}

题目列表

题目链接(Vjudge专题)
敌兵布阵        来源HDU 1166
Color the ball          来源HDU 1556
Matrix        来源POJ 2155
Counting Black        来源POJ 1656
Ultra-QuickSort        来源POJ 2299
Japan        来源POJ 3067
A Simple Problem with Integers        来源POJ 3468

敌兵布阵

类型

单点修改,区间查询

思路

树状数组板子题

代码

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <algorithm>
#include <vector>
#include <cmath>
#define endl "\n"
#define LL long long
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define debug(x); cout<<x<<endl;
using namespace std;

/***
 * Author Informathons :
 * @author: 六月陌
 * @date: 2021-04-17  13:22:06 
***/

const int maxn=1e5+10;
const int mod=1e9+7;
int t,n;
int a[maxn];
int c[maxn];

int lowbit(int x)
{
    return x&-x;
}

void update(int i,int k)
{
    while(i<=n)
    {
        c[i] += k;
        i += lowbit(i);
    }
}

int getsum(int x)
{
    int ans = 0;
    while(x>0)
    {
        ans += c[x];
        x -= lowbit(x);
    }
    return ans;
}

int main()
{
    IO;
    cin>>t;
    int CASE = 1;
    while(t--)
    {
        cout<<"Case "<<CASE++<<":\n";
        cin>>n;
        for(int i=1;i<=n;i++) 
        {
            cin>>a[i];
            update(i,a[i]);
        }
        string order;
        while(cin>>order)
        {
            int x,y;
            if(order[0] == 'E') break;
            else if(order[0] == 'A')
            {
                cin>>x>>y;
                update(x,y);
            }else if(order[0] == 'S')
            {
                cin>>x>>y;
                update(x,-y);
            }else
            {
                cin>>x>>y;
                cout<<getsum(y) - getsum(x-1)<<endl;
            }
        }
    }
    return 0;
}

Color the ball

类型

区间修改,单点查询

思路

其实只用一个差分数组统计就行了,现在主要是练习树状数组的思路。

代码

//区间修改,单点查询
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <algorithm>
#include <vector>
#include <cmath>
#define endl "\n"
#define LL long long
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define debug(x); cout<<x<<endl;
using namespace std;

/***
 * Author Informathons :
 * @author: 六月陌
 * @date: 2021-04-10  12:48:41 
***/

const int maxn=5e5+10;
const int mod=1e9+7;

int n;
int a[maxn],c[maxn];

int lowbit(int x)
{
    return x&(-x);
}

void update(int i,int j)
{
    while(i<=n)
    {
        c[i] += j;
        i += lowbit(i);
    }
}

int getsum(int i)
{
    int res = 0;
    while(i>0)
    {
        res += c[i];
        i -= lowbit(i);
    }
    return res;
}

int main()
{
    IO;
    while(cin>>n)
    {
        if(n == 0) break;
        memset(c,0,sizeof(c));
        int a,b;
        for(int i=1;i<=n;i++)
        {
            cin>>a>>b;
            update(a,1);
            update(b+1,-1);
        }
        for(int i=1;i<=n;i++)
        {
            cout<<getsum(i);
            if(i == n) cout<<endl;
            else cout<<" ";
        }
    }
    return 0;
}

Matrix

类型

区间修改,单点查询

思路

这道题其实拓展到了二维,我们可以按照一维的思路来写二维,我们首先应该明白一点,我们反转只有两个结果0变1,1变0,而且同一个点修改两次就是没修改,所以们统计每个点的修改次数就行了,最后看看是偶数还是奇数
二维矩阵修改的话看下图
二维树状数组
红色区域要加1的话相当于整体+1,然后(蓝色+绿色)区域-1,然后(绿色+黄色区域)-1,最后绿色区域+1就行了,这样每个区域都是从(0,0)开始的很好操作,可以用a[x][y]表示左上角是a[0][0]右下角是a[x][y]的一片区域。

代码

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <algorithm>
#include <vector>
#include <cmath>
#define endl "\n"
#define LL long long
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define debug(x); cout<<x<<endl;
#define lowbit(x) ((x)&(-x))
using namespace std;

/***
 * Author Informathons :
 * @author: 六月陌
 * @date: 2021-04-30  16:57:30 
***/

const int maxn=1e3+50;
const int mod=1e9+7;

int n,q,t;
int c[maxn][maxn];

void update(int x,int y,int k)
{
    for(int i=x;i<=n;i+=lowbit(i))
    {
        for(int j=y;j<=n;j+=lowbit(j))
        {
            c[i][j] += k;
        }
    }
}

int getsum(int x,int y)   //求单点的值
{
    int res = 0;
    for(int i=x;i>0;i-=lowbit(i))
    {
        for(int j=y;j>0;j-=lowbit(j))
        {
            res += c[i][j];
        }
    }
    return res;
}

int main()
{
    IO;
    cin>>t;
    while(t--)
    {
        cin>>n>>q;
        memset(c,0,sizeof(c));
        char order;
        int x1,y1,x2,y2;
        while(q--)
        {
            cin>>order;
            if(order == 'C')
            {
                cin>>x1>>y1>>x2>>y2;
                update(x1,y1,1);
                update(x1,y2+1,-1);
                update(x2+1,y1,-1);
                update(x2+1,y2+1,1);
            }else
            {
                cin>>x1>>y1;
                cout<<getsum(x1,y1)%2<<endl;
            }
        }
        if(t!=0) cout<<endl;
    }
    return 0;
}

Counting Black

类型

单点修改,区间查询

思路

这题乍一看是个区间修改,区间查询,但是由于你每次要修改的时候需要知道他之前到底是白的还是黑的,所以没啥好办法,我也只能一个一个修改了,但查询可以用树状数组。

代码

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <algorithm>
#include <vector>
#include <cmath>
#define endl "\n"
#define LL long long
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define debug(x); cout<<x<<endl;
#define lowbit(x) ((x)&(-x))
using namespace std;

/***
 * Author Informathons :
 * @author: 六月陌
 * @date: 2021-04-30  17:53:32 
***/

const int maxn=150;
const int mod=1e9+7;

int c[maxn][maxn];
bool flag[maxn][maxn];
int t;

void update(int x,int y,int k)
{
    for(int i=x;i<=maxn;i+=lowbit(i))
    {
        for(int j=y;j<=maxn;j+=lowbit(j))
        {
            c[i][j] += k;
        }
    }       
}

int getsum(int x,int y)
{
    int res = 0;
    for(int i=x;i>0;i-=lowbit(i))
    {
        for(int j=y;j>0;j-=lowbit(j))
        {
            res += c[i][j];
        }
    }
    return res;
}

int main()
{
    cin>>t;
    string order;
    int x,y,l;
    memset(c,0,sizeof(c));
    memset(flag,false,sizeof(flag));
    while(t--)
    {
        cin>>order>>x>>y>>l;
        if(order[0] == 'B')
        {
            for(int i=x;i<x+l;i++)
            {
                for(int j=y;j<y+l;j++)
                {
                    if(!flag[i][j])
                    {
                        update(i,j,1);
                        flag[i][j] = true;
                    }
                }
            }
        }else if(order[0] == 'W')
        {
            for(int i=x;i<x+l;i++)
            {
                for(int j=y;j<y+l;j++)
                {
                    if(flag[i][j])
                    {
                        update(i,j,-1);
                        flag[i][j] = false;
                    }
                }
            }
        }else
        {
            cout<<getsum(x+l-1,y+l-1)-getsum(x-1,y+l-1)-getsum(x+l-1,y-1)+getsum(x-1,y-1)<<endl;
        }
    }
    return 0;
}

Ultra-QuickSort

类型

单点修改,区间查询

思路

冒泡排序的交换次数就是求逆序对的个数。
观察一下过程就基本明白了 9 1 0 5 4
数组:9 一个元素不需要交换
数组:9 1 ——> 1 9 交换了1次
数组:1 9 0 ——> 0 1 9 交换了2次
数组:0 1 9 5 ——> 0 1 5 9 交换了1次
数组:0 1 5 9 4 ——> 0 1 4 5 9 交换了2次
数组:0 1 4 5 9 排序完成,总次数为1+2+1+2 = 6
就是每次放进来一个元素,然后排序,记录它移动了几次
如果你对冒泡排序的原理理解比较透彻的话应该很容易就能明白,这些移动次数的总和就是冒泡排序交换的总次数。 冒泡排序的时间复杂度为O(n2),如果你暴力统计上述过程的总次数的话时间复杂度也是O(n2),并没有得到优化
我们看这个变化的过程:x < y < z
x x z z z z y ——> x x y z z z z
这个y往前移动了四次,我们遍历数组得到这个答案需要O(n),但是如果我们考虑:加入y前有6个元素,前2个元素x不需要移动,我们只需把后4个元素z后移就行了。这样这个四次就可以算出来了6-2(就是加入最后一个元素前的元素个数-不需要移动的元素个数);
加入最后一个元素前的元素个数已经有了,你加入第i个的时候前面就有i-1个。
现在要算的就是不需要移动的元素个数,如果我们知道了排完后的结果(用快排之类的算法O(nlogn)就能实现),用一个新的数组b[i]表示位置i有无元素。这样你没加入一个元素就把他移动到它该到的位置,并作标记b[i] = 1;这样下次如果你想知道要移动到位置index有几个不需要移动,就只需要统计index前有几个1就行了。这个统计的过程能通过树状数组实现,修改查询复杂度都是O(logn)。
放第i个元素要移动到index的次数就是 i - 1 - getsum(index)
更新就是update(index,1)就行了。

代码

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <algorithm>
#include <vector>
#include <cmath>
#define endl "\n"
#define LL long long
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define debug(x); cout<<x<<endl;
#define lowbit(x) ((x)&(-x))
using namespace std;

/***
 * Author Informathons :
 * @author: 六月陌
 * @date: 2021-05-01  15:07:23 
***/

const int maxn=5e5+10;
const int mod=1e9+7;
struct node
{
    int num;
    int id;
}a[maxn];
int c[maxn];
int Index[maxn];
int n;
LL ans = 0;
bool cmp(node x,node y)
{
    return x.num < y.num;
}

void update(int x,int k)
{
    while(x<=n)
    {
        c[x] += k;
        x += lowbit(x);
    }
}

int getsum(int x)
{
    int res = 0;
    while(x>0)
    {
        res += c[x];
        x -= lowbit(x);
    }
    return res;
}

int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        if(n == 0) break;
        memset(c,0,sizeof(c));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i].num);
            a[i].id = i;
        }
        sort(a+1,a+n+1,cmp);
        Index[a[1].id] = 1;
        for(int i=2;i<=n;i++)
        {
            if(a[i].num == a[i-1].num) Index[a[i].id] == Index[a[i-1].id];
            else Index[a[i].id] = i;
        }
        ans = 0;
        for(int i=1;i<=n;i++)
        {
            ans += i - 1 - getsum(Index[i]);
            update(Index[i],1);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

Japan

类型

单点修改,区间查询

思路

我们把它按第一个从大到小,第二个点从小到大好序,然后开始遍历,没放一个就查第二个点前面有几个点就行了
Japan

现在放完3-1,3-2了,该看2开头的,2开头的第一个2-3前面有两个点,所以肯定会有2个焦点
Japan
再看1-4,4前面有3个点,所以会有3个交点,还不懂就看代码,看了代码就好理解了

代码

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <algorithm>
#include <vector>
#include <cmath>
#define endl "\n"
#define LL long long
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define debug(x); cout<<x<<endl;
#define lowbit(x) ((x)&(-x))
using namespace std;

/***
 * Author Informathons :
 * @author: 六月陌
 * @date: 2021-05-02  12:16:41 
***/

const int maxn=1e3+10;
const int mod=1e9+7;

struct node
{
    int b,e;
}a[maxn*maxn];
int t,n,m,k;
int c[maxn];
bool cmp(node x,node y)
{
    if(x.b == y.b)
        return x.e<y.e;
    return x.b < y.b;
}

void update(int x,int k)
{
    while(x<=m)
    {
        c[x] += k;
        x += lowbit(x);
    }
}

int getsum(int x)
{
    int res = 0;
    while(x > 0)
    {
        res += c[x];
        x -= lowbit(x);
    }
    return res;
}

int main()
{
    scanf("%d",&t);
    for(int tc=1;tc<=t;tc++)
    {
        memset(c,0,sizeof(c));
        scanf("%d%d%d",&n,&m,&k);
        int t1,t2;
        for(int i=0;i<k;i++)
        {
            scanf("%d%d",&a[i].b,&a[i].e);
        }
        sort(a,a+k,cmp);
        LL ans = 0;
        for(int i=0;i<k;i++)
        {
            ans += getsum(m) - getsum(a[i].e);
            update(a[i].e,1);
        }
        printf("Test case %d: %lld\n",tc,ans);
    } 
    return 0;
}

A Simple Problem with Integers

类型

区间修改,区间查询

思路

就是一个模板题

代码

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <algorithm>
#include <vector>
#include <cmath>
#define endl "\n"
#define LL long long
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define debug(x); cout<<x<<endl;
#define lowbit(x) ((x)&(-x))
using namespace std;

/***
 * Author Informathons :
 * @author: 六月陌
 * @date: 2021-05-02  12:53:06 
***/

const int maxn=1e5+10;
const int mod=1e9+7;
long long a[maxn];
long long c[maxn];   //存差分 a[i]-a[i-1];
long long b[maxn];    //存 i*c[i];


void update(int i,long long k)  // (a,b)+k  =>  a + k && b+1 - k
{
    int index = i;
    while(index<=maxn)
    {
        c[index] += k;
        b[index] += (i-1)*k;
        index += lowbit(index);
    }
}

long long getsum(int x)
{
    long long ans = 0;
    long long an = 0;
    int index = x;
    while(index > 0)
    {
        an += c[index];
        ans += b[index];
        index -= lowbit(index);
    }    
    ans = x*an - ans;
    return ans;
}

int main()
{
    int n,q;
    long long temp;
    memset(a,0,sizeof(a));
    memset(b,0,sizeof(b));
    memset(c,0,sizeof(c));
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        update(i,a[i]-a[i-1]);
    }
    getchar();
    char ch;
    int x,y;
    for(int i=0;i<q;i++)
    {
        scanf("%c",&ch);
        if(ch == 'Q')
        {
            scanf("%d%d",&x,&y);
            getchar();
            printf("%lld\n",getsum(y)-getsum(x-1));
        }else
        {
            scanf("%d%d%lld",&x,&y,&temp);
            getchar();
            update(x,temp);
            update(y+1,-temp);
        }
    }
    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值