ACM模板二:树、图、并查集、覆盖

该博客提供多种数据结构与算法的Java代码及测试代码,涵盖二叉树、树状数组、线段树等数据结构,以及匈牙利算法、最小生成树算法等。代码有接口注释,各部分代码有明确的编译运行规则,可独立或汇总编译运行。

目录

〇,全文说明、宏定义代码

一,二叉树

1,代码

2,测试代码

二,树状数组、线段树

1,代码

2,测试代码

三,多叉树、RMQ、LCA

1,代码

2,测试代码

四,DancingLink、Covering

1,代码

2,测试代码

五,匈牙利算法

1,代码

2,测试代码

六,并查集、无向图、最小生成树Kruskal

1,代码

2,测试代码

七,最小生成树Prim

1,代码

2,测试代码

八,有向图、单源最短路径、连通分量、拓扑排序

1,代码

2,测试代码

九,网格图、回路链路、路径重建

1,代码

2,测试代码


〇,全文说明、宏定义代码

类里面和宏定义处都有接口注释,因为宏不体现具体参数,所以注释以类里面的为准。

代码工程结构:

  • 每一章的第一节《代码》可以独立编译运行(本地要加LOCAL宏)
  • 每一章的第二节《测试代码》搭配第〇章的代码和本章第一节的代码可以编译运行并测试成功
  • 所有模板文章的第〇章的代码和其他章第一节的《代码》全部汇总起来可以编译运行

宏定义代码:


#define LOCAL //力扣不要本行代码,其他OJ随意

///(1)二叉树///
#define MaxDepth BinaryTree::maxDepth//求二叉树的深度,根节点深度是1
#define MinDepth BinaryTree::minDepth//叶子节点的最小深度
#define PreorderTraversal BinaryTree::preorderTraversal//二叉树的前序遍历
#define PostorderTraversal BinaryTree::postorderTraversal//二叉树的后序遍历
#define InorderTraversal BinaryTree::inorderTraversal//二叉树的中序遍历
#define LevelOrderTraversal BinaryTree::levelOrderTraversal//二叉树的层序遍历
#define BuildTreePre BinaryTree::buildTreePre//根据前序和中序重建二叉树,假设没有重复数字
#define BuildTreePost BinaryTree::buildTreePost//根据中序和后序重建二叉树,假设没有重复数字
#define CountNodes BinaryTree::countNodes//求节点个数
#define CopyTree BinaryTree::copyTree//拷贝二叉树
#define IsSameTree BinaryTree::isSameTree//判断两棵二叉树是否全等
#define InvertTree BinaryTree::invertTree//左右翻转二叉树

///(2)树状数组、线段树///
// TreeArray 树状数组
// TreeArray2D 二维树状数组
// SegmentTree 线段树

///(3)多叉树、RMQ、LCA///
#define MultiTreePreorder MultiTree::preorder //多叉树前序遍历
#define MultiTreePostorder MultiTree::postorder //多叉树后序遍历
#define EdgesToMultiTree MultiTree::edgesToMultiTree//输入无向图,构造多叉生成树
// TreeWithId 略。//把二叉树或多叉树转成id形式的树,前序遍历,从0开始编号
// RMQ 略。
// LCA 略。

///(4)DancingLink、Covering///
// DancingLink 略。精确覆盖算法
#define GetEdgeCover Covering::getEdgeCover//给定一个2n个点的图,选出n条边,刚好覆盖这2n个点

///(5)匈牙利算法///
#define HungarianMaxMatch Hungarian().maxMatch //求二分图最大匹配,v是一边的点集,adjaList的每条边都恰好有一个点在v中

///(6.1)并查集///
// Union 略。并查集
// UnionDif 略。
// Vunion 略。

///(6.2)无向图///
#define GetAdjaListFromTree UndirectedGraph::getAdjaList //输入二叉树,转化成无向图的邻接表,没有权值,节点编号是先序遍历的编号,默认从0开始
#define EdgesToBinaryTree UndirectedGraph::edgesToBinaryTree //输入无环无向图,生成任意一棵二叉树,val存放原id,节点编号是从0开始依次编号
#define UndirectedEdgeToFatherList UndirectedGraph::undirectedEdgeToFatherList //无向拓扑排序,输入无向无环图{
  
  {1,2}{1,3}{4,3}}和指定根1,输出父节点表{4:3, 3:1, 2:1}
#define HasUndirectedCircle UndirectedGraph::hasUndirectedCircle //判断无向图是否有环
#define IsCompleteGraph UndirectedGraph::isCompleteGraph //判断是不是k-正则图
#define IsTwoDivGraph UndirectedGraph::isTwoDivGraph //判断是不是二分图

///(6.3)最小生成树Kruskal///
#define KruskalMinCostTree Kruskal::kruskalMinCostTree

///(7)最小生成树Prim///
#define PrimminCostTree Prim::minCostConnectPoints


///(8.1)有向图///
#define ReverseGraph DirectedGraph::reverseGraph//构建有向图的反向图
#define GetLongestPath DirectedGraph::getLongestPath//求有向无环图中的最长路径长度,出参nextNode是每个点的后继,len是每个点出发的最长路径长度
#define HasDirectedCircle DirectedGraph::hasCircle//根据有向图的邻接表判断是否有环

///(8.2)单源最短路径///
#define DijskraShortestPath Dijskra::shortestPath//求最短路,适用于不存在负权值的边的图
#define BellmanFordShortestPath BellmanFord::shortestPath//求最短路,适用于不存在负权值的环的图
#define SPFAShortestPath SPFA::shortestPath//求最短路,适用于不存在负权值的环的图

///(8.3)不区分有向图和无向图的通用操作///
#define GetSubGraph GraphOpt::getSubGraph//根据点集取子图

///(8.4)连通分量、拓扑排序///
#define SemiConnectComponent SemiConnect::semiConnectComponent//半连通分量分割
#define ConnectComponent KosarajuStrongConnect::connectComponent//Kosaraju算法,强连通分量分割
#define GetPointGraph KosarajuStrongConnect::getPointGraph//强连通分量缩点
// TarjanUndirect 略。Tarjan算法,双连通分量分割
// TarjanStrongConnect 略。Tarjan算法,强连通分量分割
#define TopoSortNoCircle DirectedTopoSort::topoSortNoCircle //有向无环图拓扑排序,输入n=3,g.edges={
  
  {0,1}{0,2}{1,2}}, 输出{0,1,2},有环则输出为空
#define TopoSort DirectedTopoSort::topoSort //有向图拓扑排序


///(9.1)网格图///
// GridGraph 略

///(9.2)回路或链路///
// Hierholzer 略。欧拉回路或链路
// Hamilton 略。哈密顿回路或链路

///(9.3)路径重建///
// ReBuild 略。路径重建

一,二叉树

1,代码


#ifdef LOCAL
#ifndef struct_TreeNode
#define struct_TreeNode
struct TreeNode {
	int val;
	TreeNode* left;
	TreeNode* right;
	TreeNode() : val(0), left(nullptr), right(nullptr) {}
	TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
	TreeNode(int x, TreeNode* left, TreeNode* right) : val(x), left(left), right(right) {}
};
#endif
#endif

class BinaryTree
{
public:
	//求二叉树的深度,根节点深度是1
	static int maxDepth(TreeNode* root) {
		if (!root)return 0;
		return max(maxDepth(root->left), maxDepth(root->right)) + 1;
	}
	//叶子节点的最小深度
	static int minDepth(TreeNode* root) {
		if (!root)return 0;
		return min_depth(root);
	}
	//二叉树的前序遍历
	static vector<int> preorderTraversal(TreeNode* root) {
		vector<int>v1;
		if (root == NULL)return v1;
		v1.insert(v1.end(), root->val);
		vector<int>v2 = preorderTraversal(root->left);
		v1.insert(v1.end(), v2.begin(), v2.end());
		v2 = preorderTraversal(root->right);
		v1.insert(v1.end(), v2.begin(), v2.end());
		return v1;
	}
	//二叉树的后序遍历
	static vector<int> postorderTraversal(TreeNode* root) {
		vector<int>v1;
		if (root == NULL)return v1;
		vector<int>v2 = postorderTraversal(root->left);
		v1.insert(v1.end(), v2.begin(), v2.end());
		v2 = postorderTraversal(root->right);
		v1.insert(v1.end(), v2.begin(), v2.end());
		v1.insert(v1.end(), root->val);
		return v1;
	}
	//二叉树的中序遍历
	static vector<int> inorderTraversal(TreeNode* root) {
		vector<int>v1;
		if (root == NULL)return v1;
		v1 = inorderTraversal(root->left);
		v1.insert(v1.end(), root->val);
		vector<int>v2 = inorderTraversal(root->right);
		v1.insert(v1.end(), v2.begin(), v2.end());
		return v1;
	}
	//二叉树的层序遍历
	static vector<vector<int>> levelOrderTraversal(TreeNode* root) {
		vector<vector<int>>ans;
		if (!root)return ans;
		vector<vector<TreeNode*>>v;
		vector<TreeNode*>tmp;
		vector<int>tmpans;
		tmp.push_back(root);
		v.push_back(tmp);
		bfs(v);
		for (int i = 0; i < v.size(); i++) {
			tmpans.resize(v[i].size());
			for (int j = 0; j < v[i].size(); j++)tmpans[j] = v[i][j]->val;
			ans.push_back(tmpans);
		}
		return ans;
	}
	//根据前序和中序重建二叉树,假设没有重复数字
	static TreeNode* buildTreePre(vector<int>& preorder, vector<int>& inorder) {
		return build_tree(preorder, 0, inorder, 0, inorder.size());
	}
	//根据中序和后序重建二叉树,假设没有重复数字
	static TreeNode* buildTreePost(vector<int>& inorder, vector<int>& postorder) {
		return build_tree2(postorder, 0, inorder, 0, inorder.size());
	}
	//求节点个数
	static int countNodes(TreeNode* root) {
		if (!root)return 0;
		return countNodes(root->left) + countNodes(root->right) + 1;
	}
	//拷贝二叉树
	static TreeNode* copyTree(TreeNode* root)
	{
		if (!root)return root;
		return new TreeNode(root->val, copyTree(root->left), copyTree(root->right));
	}
	//判断两棵二叉树是否全等
	static bool isSameTree(TreeNode* r1, TreeNode* r2)
	{
		if (r1 == NULL && r2 == NULL)return true;
		if (r1 == NULL || r2 == NULL)return false;
		if (r1->val != r2->val)return false;
		return isSameTree(r1->left, r2->left) && isSameTree(r1->right, r2->right);
	}
	//左右翻转二叉树
	static TreeNode* invertTree(TreeNode* root) {
		if (!root)return root;
		TreeNode* p = root->left, *q = root->right;
		root->left = q, root->right = p;
		invertTree(p);
		invertTree(q);
		return root;
	}
private:
	static int min_depth(TreeNode* root) {
		if (!root)return 1234567890;
		if (!root->left && !root->right)return 1;
		return min(min_depth(root->left), min_depth(root->right)) + 1;
	}
	static void bfs(vector<vector<TreeNode*>>&v) {
		vector<TreeNode*>v1 = *(v.end() - 1);
		vector<TreeNode*>v2;
		for (int i = 0; i < v1.size(); i++) {
			if (v1[i]->left)v2.push_back(v1[i]->left);
			if (v1[i]->right)v2.push_back(v1[i]->right);
		}
		if (v2.empty())return;
		v.push_back(v2);
		bfs(v);
	}
	static TreeNode* build_tree(vector<int>& preorder, int s1, vector<int>& inorder, int s2, int len) {
		if (len <= 0)return NULL;
		TreeNode* ans = new TreeNode;
		ans->val = preorder[s1];
		auto loc = find(inorder.begin() + s2, inorder.begin() + s2 + len, preorder[s1]);
		ans->left = build_tree(preorder, s1 + 1, inorder, s2, loc - inorder.begin() - s2);
		ans->right = build_tree(preorder, loc - inorder.begin() - s2 + s1 + 1, inorder, loc - inorder.begin() + 1, len - (loc - inorder.begin() - s2) - 1);
		return ans;
	}
	static TreeNode* build_tree2(vector<int>& postorder, int s1, vector<int>& inorder, int s2, int len) {
		if (len <= 0)return NULL;
		TreeNode* ans = new TreeNode;
		ans->val = postorder[s1 + len - 1];
		auto loc = find(inorder.begin() + s2, inorder.begin() + s2 + len, postorder[s1 + len - 1]);
		ans->left = build_tree2(postorder, s1, inorder, s2, loc - inorder.begin() - s2);
		ans->right = build_tree2(postorder, loc - inorder.begin() - s2 + s1, inorder, loc - inorder.begin() + 1, len - (loc - inorder.begin() - s2) - 1);
		return ans;
	}
};

2,测试代码



template<typename T>
static bool IsSame(const T &a, const T &b)
{
	return a == b;
}
template<typename T>
static bool IsSame(const vector<T>& v1, const vector<T>& v2)
{
	if (v1.size() - v2.size())return false;
	for (int i = 0; i < v1.size(); i++)if (!IsSame(v1[i], v2[i]))return false;
	return true;
}
template<typename T, typename T2>
static bool IsSame(const pair<T,T2>&p1, const pair<T, T2>&p2)
{
	return IsSame(p1.first, p2.first) && IsSame(p1.second, p2.second);
}

#define EXPECT_EQ(a,b) if(!IsSame(a,b)){cout<<"ERROR!!!!!!!!!\n";return false;}


bool testBinaryTree()
{
	TreeNode t1(1), t2(2), t3(3), t4(4);
	t1.left = &t2, t1.right = &t3, t2.left = &t4;
	auto p = &t1;
	EXPECT_EQ(MaxDepth(p), 3);
	EXPECT_EQ(MinDepth(p), 2);
	vector<int>pre{ 1, 2, 4, 3 }, post{ 4, 2, 3, 1 }, inorder{ 4, 2, 1, 3 };
	EXPECT_EQ(PreorderTraversal(p), pre);
	EXPECT_EQ(PostorderTraversal(p), post);
	EXPECT_EQ(InorderTraversal(p), inorder);
	auto p2 = BuildTreePre(pre, inorder);
	EXPECT_EQ(IsSameTree(p, p2), true);
	p2 = BuildTreePost(inorder, post);
	EXPECT_EQ(IsSameTree(p, p2), true);
	EXPECT_EQ(CountNodes(p), 4);
	p2 = CopyTree(p);
	EXPECT_EQ(IsSameTree(p, p2), true);
	InvertTree(p2);
	EXPECT_EQ(IsSameTree(p, p2), false);
	InvertTree(p2);
	EXPECT_EQ(IsSameTree(p, p2), true);
	return true;
}

int main()
{
	if (testBinaryTree())cout << "test succ!";
	return 0;
}

二,树状数组、线段树

1,代码


template<int maxLen = 100000>
class TreeArray
{
public:
	TreeArray(int len)//len是元素实际数量,元素id范围是[1,n]
	{
		this->n = len;
		memset(c, 0, sizeof(int)*(len + 1));
	}
	void add(int i, int x)
	{
		while (i <= n)
		{
			c[i] += x;
			i += (i&(-i));
		}
	}
	int getSum(int i)
	{
		int s = 0;
		while (i)
		{
			s += c[i];
			i -= (i&(-i));
		}
		return s;
	}
private:
	int n;
	int c[maxLen+5];
};

template<int maxLen = 1000>
class TreeArray2D
{
public:
	TreeArray2D(int len)//len是元素实际数量,元素id范围是[1,n]*[1,n]
	{
		this->n = len;
		for (int i = 0; i <= n; i++)memset(c[i], 0, sizeof(int)*(n + 1));
	}
	void add(int x, int y, int a = 1)
	{
		for (int i = x; i <= n; i += (i&(-i)))
			for (int j = y; j <= n; j += (j&(-j)))c[i][j] += a;
	}
	int getSum(int x, int y)
	{
		int s = 0;
		for (int i = x; i > 0; i -= (i&(-i)))
			for (int j = y; j > 0; j -= (j&(-j)))
				s += c[i][j];
		return s;
	}
private:
	int n;
	int c[maxLen +5][maxLen +5];
};

//type=0,1,2,3,4分别表示sum型、min型、max型、minId型、maxId型线段树
//maxLen是元素最大数量
template<int type, int maxLen = 100000, typename T = int>
class SegmentTreeBase
{
public:
	T* getData()//先调getData更新数据再调build
	{
		return num;
	}
	void build(int len)//len是元素实际数量,元素id范围是[1,n]
	{
		this->n = len;
		build(1, 1, n);
	}
	void update(int uplace, T value)//1<=uplace<=n
	{
		num[uplace] = value;
		update(1, 1, n, uplace);
	}
	T query(int x, int y)//1<=x<=y<=n
	{
		return query(1, 1, n, x, y);
	}
protected:
	template<typename T2>
	inline T2 op(T2 a, T2 b)
	{
		if (type == 3)return num[a] < num[b] ? a : b;
		if (type == 4)return num[a] > num[b] ? a : b;
		if (type == 0)return a + b;
		return type == 1 ? min(a, b) : max(a, b);
	}
	void build(int key, int low, int high)
	{
		if (low == high)
		{
			ans[key] = type > 2 ? low : num[low];
			return;
		}
		int mid = (low + high) / 2;
		build(key * 2, low, mid);
		build(key * 2 + 1, mid + 1, high);
		ans[key] = op(ans[key * 2], ans[key * 2 + 1]);
	}
	void update(int key, int low, int high, int uplace)
	{
		if (low == high)
		{
			ans[key] = type > 2 ? low : num[low];
			return;
		}
		int mid = (low + high) / 2;
		if (uplace <= mid)update(key * 2, low, mid, uplace);
		else update(key * 2 + 1, mid + 1, high, uplace);
		ans[key] = op(ans[key * 2], ans[key * 2 + 1]);
	}
	T query(int key, int low, int high, int x, int y)
	{
		if (low == x && high == y)return ans[key];
		int mid = (low + high) / 2;
		if (mid < x)return query(key * 2 + 1, mid + 1, high, x, y);
		if (mid >= y)return query(key * 2, low, mid, x, y);
		T a = query(key * 2, low, mid, x, mid);
		T b = query(key * 2 + 1, mid + 1, high, mid + 1, y);
		return  op(a, b);
	}
protected:
	int n;
	T num[maxLen + 1];
	T ans[maxLen * 4 + 10];
};
//sum型线段树拓展,支持查询前缀和
template<int maxLen = 100000, typename T = int>
class SegmentTreeTypeSum :public SegmentTreeBase<0, maxLen, T>
{
	using BASE = SegmentTreeBase<0, maxLen, T>;
public:
	int queryPreSum(T x)
	{
		return queryPreSum(1, 1, BASE::n, x);
	}
private:
	int queryPreSum(int key, int low, int high, T x)
	{
		if (low == high)return low;
		int mid = (low + high) / 2;
		if (x <= BASE::ans[key * 2])return queryPreSum(key * 2, low, mid, x);
		return queryPreSum(key * 2 + 1, mid + 1, high, x - BASE::ans[key * 2]);
	}
};
//5种线段树拓展,支持区间更新,区间查询
template<int type, int maxLen = 100000, typename T = int, T invalid = -1>
class SegmentTree :public SegmentTreeBase<type, maxLen, T>
{
	using BASE = SegmentTreeBase<type, maxLen, T>;
public:
	void build(int len)//len是元素实际数量,元素id范围是[1,n]
	{
		BASE::n = len;
		build(1, 1, BASE::n);
	}
	void update(int uplace, T value)//1<=uplace<=n,覆盖父类函数
	{
		update(uplace, uplace, value);
	}
	void update(int x, int y, T value)//1<=x<=y<=n
	{
		update(1, 1, BASE::n, x, y, value);
	}
	T query(int x, int y)//1<=x<=y<=n
	{
		return query(1, 1, BASE::n, x, y);
	}
private:
	static inline T opMulti(T a, int num)
	{
		if (!type)return a * num;
		return a;
	}
	void build(int key, int low, int high)
	{
		if (low == high)
		{
			BASE::ans[key] = type > 2 ? low : BASE::num[low];
			lazy[key] = invalid;
			return;
		}
		int mid = (low + high) / 2;
		build(key * 2, low, mid);
		build(key * 2 + 1, mid + 1, high);
		BASE::ans[key] = BASE::op(BASE::ans[key * 2], BASE::ans[key * 2 + 1]);
		lazy[key] = invalid;
	}
	void update(int key, int low, int high, int x, int y, T value)
	{
		if (low == x && high == y)
		{
			BASE::ans[key] = type > 2 ? x : opMulti(value, high - low + 1);
			lazy[key] = value;
			if (x == y)BASE::num[x] = value;
			return;
		}
		if (lazy[key] != invalid)down(key, low, high);
		int mid = (low + high) / 2;
		if (mid < x)update(key * 2 + 1, mid + 1, high, x, y, value);
		else if (mid >= y)update(key * 2, low, mid, x, y, value);
		else
		{
			update(key * 2, low, mid, x, mid, value);
			update(key * 2 + 1, mid + 1, high, mid + 1, y, value);
		}
		BASE::ans[key] = BASE::op(BASE::ans[key * 2], BASE::ans[key * 2 + 1]);
	}
	void down(int key, int low, int high)
	{
		int mid = (low + high) / 2;
		BASE::ans[key * 2] = type > 2 ? low : opMulti(lazy[key], mid - low + 1);
		BASE::ans[key * 2 + 1] = type > 2 ? high : opMulti(lazy[key], high - mid);
		lazy[key * 2] = lazy[key];
		lazy[key * 2 + 1] = lazy[key];
		lazy[key] = invalid;
	}
	T query(int key,
1 论 3 1.1 术语 3 1.2 独立集、覆盖集、支配集之间关系 3 1.3 DFS 4 1.3.1 割顶 6 1.3.2 桥 7 1.3.3 强连通分量 7 1.4 最小点基 7 1.5 拓扑排序 7 1.6 欧拉路 8 1.7 哈密顿路(正确?) 9 1.8 Bellman-ford 9 1.9 差分约束系统(用bellman-ford解) 10 1.10 dag最短路径 10 1.11 匹配 11 1.11.1 匈牙利算法 11 1.11.2 KM算法 12 1.12 网络流 15 1.12.1 最大流 15 1.12.2 上下界的网络的最大流 17 1.12.3 上下界的网络的最小流 17 1.12.4 最小费用最大流 18 1.12.5 上下界的网络的最小费用最小流 21 2 数论 21 2.1 最大公约数gcd 21 2.2 最小公倍数lcm 22 2.3 快速幂取模B^LmodP(O(logb)) 22 2.4 Fermat小定理 22 2.5 Rabin-Miller伪素数测试 22 2.6 Pollard-rho 22 2.7 扩展欧几里德算法extended-gcd 24 2.8 欧拉定理 24 2.9 线性同余方程ax≡b(mod n) 24 2.10 中国剩余定理 25 2.11 Discrete Logging(BL == N (mod P)) 26 2.12 N!最后一个不为0的数字 27 2.13 2^14以内的素数 27 3 数据结构 31 3.1 堆(最小堆) 31 3.1.1 删除最小值元素: 31 3.1.2 插入元素和向上调整: 32 3.1.3 堆的建立 32 3.2 并查集 32 3.3 状数组 33 3.3.1 LOWBIT 33 3.3.2 修改a[p] 33 3.3.3 前缀和A[1]+…+A[p] 34 3.3.4 一个状数组的程序 34 3.4 线段 35 3.5 字符串 38 3.5.1 字符串哈希 38 3.5.2 KMP算法 40 4 计算几何 41 4.1 直线交点 41 4.2 判断线段相交 41 4.3 三点外接圆圆心 42 4.4 判断点在多边形内 43 4.5 两圆交面积 43 4.6 最小包围圆 44 4.7 经纬度坐标 46 4.8 凸包 46 5 Problem 48 5.1 RMQ-LCA 48 5.1.1 Range Minimum Query(RMQ) 49 5.1.2 Lowest Common Ancestor (LCA) 53 5.1.3 Reduction from LCA to RMQ 56 5.1.4 From RMQ to LCA 57 5.1.5 An<O(N), O(1)> algorithm for the restricted RMQ 60 5.1.6 An AC programme 61 5.2 最长公共子序列LCS 64 5.3 最长上升子序列/最长不下降子序列(LIS) 65 5.3.1 O(n^2) 65 5.3.2 O(nlogn) 66 5.4 Joseph问题 67 5.5 0/1背包问题 68 6 组合数学相关 69 6.1 The Number of the Same BST 69 6.2 排列生成 71 6.3 逆序 72 6.3.1 归并排序求逆序 72 7 数值分析 72 7.1 分法 72 7.2 迭代法(x=f(x)) 73 7.3 牛顿迭代 74 7.4 数值积分 74 7.5 高斯消元 75 8 其它 77
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值