Tarjan强连通分量算法

【图论】Tarjan算法(强连通分量) Tarjan算法是一种由美国计算机科学家罗伯特·塔杨(Robert Tarjan)提出的求解有向图强连通分量的线性时间的算法。 阅读详情

前言

“Tarjan 陪伴强连通分量
生成树完成后思路才闪光
欧拉跑过的七桥古塘
让你 心驰神往”
——《膜你抄》

引入

什么是强联通分量(SCC)呢?

有向图强连通分量:在有向图 G G G 中,如果两个顶点 v i , v j v_i,v_j vi​,vj​ 间( v i > v j v_i>v_j vi​>vj​)有一条从 v i v_i vi​ 到 v j v_j vj​ 的有向路径,同时还有一条从 v j v_j vj​ 到 v i v_i vi​ 的有向路径,则称两个顶点强连通(strongly connected)。如果有向图 G G G 的每两个顶点都强连通,称 G G G 是一个强连通图。有向图的极大强连通子图,称为强连通分量(strongly connected components)。

看下面这个有向图:

请添加图片描述
它有 3 3 3 个强联通分量,如图:
请添加图片描述
显然,任何一个强连通分量之中的所有点之间均可到达。

那我们怎么求出一个图中的强连通分量呢?

直接DFS?显然会超时。

对于这个问题,美国计算机科学家Robert Tarjan(罗伯特·塔扬)提出了著名的Tarjan算法。

【题外话】

CSP之前膜拜大佬增加RP:

Robert Tarjan

Tarjan算法

首先,我们应该了解一下DFS生成树,即按照DFS次序形成的树。

以下选自OI Wiki


请添加图片描述
有向图的 DFS 生成树主要有 4 种边(不一定全部出现):

  1. 树边(tree edge):示意图中以黑色边表示,每次搜索找到一个还没有访问过的结点的时候就形成了一条树边。
  2. 反祖边(back edge):示意图中以红色边表示(即 7 → 1 7\rightarrow1 7→1),也被叫做回边,即指向祖先结点的边。
  3. 横叉边(cross edge):示意图中以蓝色边表示(即 9 → 7 9\rightarrow7 9→7),它主要是在搜索的时候遇到了一个已经访问过的结点,但是这个结点 并不是 当前结点的祖先时形成的。
  4. 前向边(forward edge):示意图中以绿色边表示(即 3 → 6 3\rightarrow6 3→6),它是在搜索的时候遇到子树中的结点的时候形成的。

以上选自OI Wiki

对于文章开头的那张图,我们尝试对它进行DFS,组成DFS树:
请添加图片描述

我们发现,那条反祖边(红边)指向了DFS树的根,形成了一个环,环是强连通的。

但是,我们怎么知道这个环是不是强联通分量呢?

很显然,我们只需要找到一条终点是DFS树的根,起点尽量靠下的反祖边,这条反祖边形成的一定是SCC。

注意,这里说的DFS树不一定是整个图的DFS树,也可以是子图的DFS树。

然而,我们又怎么确定哪些点是在SCC中呢?

如果结点 u u u 是某个强连通分量在搜索树中遇到的第一个结点,那么这个强连通分量的其余结点肯定是在搜索树中以 u u u 为根的子树中。 ——OI Wiki

为什么呢?

即得易见平凡,仿照上例显然,留作习题答案略,读者自证不难。反之亦然同理,推论自然成立,略去过程QED ,由上可知证毕。
——《西江月·证明》

我们假设一个点 x x x 在SCC中却不在以 u u u 为根的子树中,那么从 u u u 到 x x x 的过程中一定有一个反祖边或横叉边使其在子树外,而根据定义,这两种边都在 u u u 之前被搜索到,与“结点 u u u 是某个强连通分量在搜索树中遇到的第一个结点”矛盾,得证。

我们只需要开一个栈,从树根开始,把遍历到的点都加到栈里。当我们发现一个SCC存在时,就不断地弹出栈,直到弹出DFS树的树根为止。这样,弹出的节点就是SCC中的节点。

那么,算法的基本原理已经了解了,我们来具体地实现一下。

首先,我们要记录每个节点是第几个被搜索到的(即时间戳),记作 d f n [ x ] dfn[x] dfn[x]。

然后,我们要记录每个节点所能到达的 d f n dfn dfn 最小的节点的 d f n dfn dfn ,记作 l o w [ x ] low[x] low[x]。

我们还要开一个栈,将遍历到的所有点加入栈中。

这样一来,如果我们跑完DFS,发现根节点的 d f n dfn dfn 等于 l o w low low,则说明根节点又回到了自身。这时我们就可以依次弹出栈中的节点,加入这个强连通分量,知道弹出根为止。

我们模拟一下下图:
请添加图片描述
从 1 1 1 开始遍历, d f n [ 1 ] = l o w [ 1 ] = 1 dfn[1]=low[1]=1 dfn[1]=low[1]=1, 1 1 1 入栈;
1 → 2 1\rightarrow2 1→2, d f n [ 2 ] = l o w [ 2 ] = 2 dfn[2]=low[2]=2 dfn[2]=low[2]=2, 2 2 2 入栈;
2 → 3 2\rightarrow3 2→3, d f n [ 3 ] = l o w [ 3 ] = 3 dfn[3]=low[3]=3 dfn[3]=low[3]=3, 3 3 3 入栈;
3 → 4 3\rightarrow4 3→4, d f n [ 4 ] = l o w [ 4 ] = 4 dfn[4]=low[4]=4 dfn[4]=low[4]=4, 4 4 4 入栈;
4 4 4 的子树遍历完了,发现 d f n [ 4 ] = l o w [ 4 ] dfn[4]=low[4] dfn[4]=low[4], 4 4 4 出栈, 4 4 4 自己为一个SCC;
3 3 3 的子树遍历完了,发现 d f n [ 3 ] = l o w [ 3 ] dfn[3]=low[3] dfn[3]=low[3], 3 3 3 出栈, 3 3 3 自己为一个SCC;
2 → 5 2\rightarrow5 2→5, d f n [ 5 ] = l o w [ 5 ] = 5 dfn[5]=low[5]=5 dfn[5]=low[5]=5, 5 5 5 入栈;
5 → 1 5\rightarrow1 5→1, l o w [ 5 ] = 1 low[5]=1 low[5]=1,向前转移, l o w [ 2 ] = 1 low[2]=1 low[2]=1;
2 2 2 的子树遍历完了,发现 d f n [ 2 ] ≠ l o w [ 2 ] dfn[2]\ne low[2] dfn[2]​=low[2],不操作;
1 1 1 的子树遍历完了,发现 d f n [ 1 ] = l o w [ 1 ] dfn[1]=low[1] dfn[1]=low[1], 5 5 5 出栈, 2 2 2 出栈, 1 1 1 出栈, 1 , 2 , 5 1,2,5 1,2,5 为一个SCC,结束。

具体代码实现呢?

void tarjan(int x)
{
	dfn[x]=low[x]=++times;//时间戳+1并记录,low先设为times
	vis[x]=1;//标记为已入栈
	sta.push(x);
	for(int i=head[x];~i;i=edge[i].nxt)
	{
		if(!dfn[edge[i].to])//如果没搜过
		{
			tarjan(edge[i].to);//搜它
			low[x]=min(low[x],low[edge[i].to]);//更新
		}
		else if(vis[edge[i].to])//如果在栈中
			low[x]=min(low[x],dfn[edge[i].to]);//更新
	}//如果这个节点被搜过却没在栈中,说明它已经在别的SCC中了,不用处理
	if(dfn[x]==low[x])//找到SCC
	{
		tot++;//SCC总数+1
		while(1)
		{
			int t=sta.top();
			sta.pop();
			vis[t]=0;
			scc[t]=tot;
			if(t==x)
				break;
		}
	}
}

但是我们不确定给定的图是否是联通的,所以通常我们将所有的点循环作为DFS的根,如下:

for(int i=1;i<=n;i++)
	if(!dfn[i])
		tarjan(i);

还有不理解的地方可以看后面例题代码。

例题

洛谷 P3387 【模板】缩点

给定一个 n n n 个点 m m m 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。

显然,一个SCC中的点都可以到达,那么我们可以将一个SCC缩为一个点,点权为SCC中的点的点权之和。同时将缩成的点重新连边,这时形成的新图一定是一个DAG(有向无环图)。我们在DAG上跑一遍拓扑排序,按照拓扑序进行DP即可。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
#include<queue>
#define MAXN 10010
#define MAXM 100010
using namespace std;
struct Edge
{
	int from;
	int to;
	int nxt;
}
edge[MAXM];
int head[MAXN],size;
void add(int from,int to)
{
	edge[++size].nxt=head[from];
	edge[size].from=from;
	edge[size].to=to;
	head[from]=size;
}
struct Edge_//新边
{
	int to;
	int nxt;
}
edge_[MAXM];
int head_[MAXN],size_;
void add_(int from,int to)
{
	edge_[++size_].nxt=head_[from];
	edge_[size_].to=to;
	head_[from]=size_;
}
void init()
{
	memset(edge,-1,sizeof(edge));
	memset(head,-1,sizeof(head));
	memset(edge_,-1,sizeof(edge_));
	memset(head_,-1,sizeof(head_));
}
int n,m;
int a[MAXN];
int u,v;
int dfn[MAXN],low[MAXN];
bool vis[MAXN];
int dis[MAXN];//新点权
int scc[MAXN],tot;
int times;
stack<int> sta;
void tarjan(int x)
{
	dfn[x]=low[x]=++times;
	vis[x]=1;
	sta.push(x);
	for(int i=head[x];~i;i=edge[i].nxt)
	{
		if(!dfn[edge[i].to])
		{
			tarjan(edge[i].to);
			low[x]=min(low[x],low[edge[i].to]);
		}
		else if(vis[edge[i].to])
			low[x]=min(low[x],dfn[edge[i].to]);
	}
	if(dfn[x]==low[x])
	{
		tot++;
		while(1)
		{
			int t=sta.top();
			sta.pop();
			vis[t]=0;
			scc[t]=tot;
			dis[tot]+=a[t];//处理点权
			if(t==x)
				break;
		}
	}
}
int in[MAXN];
int top[MAXN],cnt;
void topsort()//拓扑排序,便于DP
{
	queue<int> q;
	for(int i=1;i<=tot;i++)
		if(in[i]==0)
			q.push(i);
	while(!q.empty())
	{
		u=q.front();
		q.pop();
		top[++cnt]=u;
		for(int i=head_[u];~i;i=edge_[i].nxt)
		{
			in[edge_[i].to]--;
			if(in[edge_[i].to]==0)
				q.push(edge_[i].to);
		}
	}
}
int f[MAXN];
int ans;
int main()
{
	init();
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&u,&v);
		add(u,v);
	}
	for(int i=1;i<=n;i++)
		if(!dfn[i])
			tarjan(i);
	for(int i=1;i<=size;i++)//缩点,连边
	{
		u=edge[i].from;
		v=edge[i].to;
		if(scc[u]==scc[v])
			continue;
		add_(scc[u],scc[v]);
		in[scc[v]]++;
	}
	topsort();
	for(int i=1;i<=tot;i++)
		f[i]=dis[i];
	for(int i=1;i<=cnt;i++)//DP
		for(int j=head_[top[i]];~j;j=edge_[j].nxt)
			if(top[i]!=edge_[j].to)
				f[edge_[j].to]=max(f[edge_[j].to],f[top[i]]+dis[edge_[j].to]);
	for(int i=1;i<=tot;i++)
		ans=max(ans,f[i]);
	printf("%d",ans); 
	return 0;
} 

后记

在写这篇文章的时候思路很清晰,却不知从何讲起,查阅了很多资料才完成了这篇文章。事实上,我在文章中引用了很多OI Wiki中的片段,因为我觉得OI Wiki中的讲解更加的严谨。

在这里,我将我所查阅的几份主要资料放上:

百度百科 - 强连通分量

OI Wiki - 强连通分量

洛谷 P3387 题解

谢谢大家的阅读!

算法学习笔记(Tarjan) 本文介绍Tarjan求强联通分量、找割点和割边、找环。 阅读详情

相关推荐

数据结构——强连通分量图算法

介绍图论中的强连通分量算法

m0_55369638的博客 1116

Tarjan求强连通分量

[算法定义] 在有向图中,如果两个顶点至少存在一条路径(可以相互通达),则称两个顶点强连通(strongly connected)。 如果有向图G的每两个顶点都强连通,称G是一个强连通图。 非强连通有向图的极大强连通子图,称为强连通分量(strongly connected components)。 在上图中,{1 , 2 , 3 , 4 } , { 5 } , { 6 } 三个区域...

weixin_44959460的博客 5280

tarjan算法求解强连通分量

强连通分量是有向图中的概念。 在有向图中,若任意两个顶点都是连通的,那么就是强连通图,非强连通图中的强连通子图称为强连通分量。 可以用tarjan算法求解,任选一个节点作为dfs树的根节点,注意到对于节点u,若子树中的任意节点无回边到节点u的祖先(但是回到u),则子树以及u节点为一个强连通分量,也就是能通过u访问u子树的任意一个节点,但是没有任意一个节点可以返回u节点。当然该子树。 对于图1

BeingLucky的博客 496

图论算法-Tarjan之强连通分量

Tarjan--强连通分量

weixin_49289750的博客 1487

tarjan 算法求强连通分量

转自大牛 全网最详细tarjan算法讲解,我不敢说别的。反正其他tarjan算法讲解,我看了半天才看懂。我写的这个,读完一遍,发现原来tarjan这么简单! tarjan算法,一个关于 图的联通性的神奇算法。基于DFS(迪法师)算法,深度优先搜索一张有向图。!注意!是有向图。根据树,堆栈,打标记等种种神(che)奇(dan)方法来完成剖析一个图的工作。而图的联通性,就是

Marcus-Bao的个人主页 932

「分治法」最近点对距离问题

一、问题描述 给定平面S上n个点,找其中的一对点,使得在n个点组成的 所有点对中,该点对间的距离最小。 假设所讨论的点是以标准笛卡儿坐标形式(x, y)给出的。因 此,在两个点Pi =(xi , yi )和Pj =(xj , yj )之间的距离是标准的欧几里德 距离: d = 根号下( x i − x j ) 2 + ( y i − y j )2 二、问题分析 直接用暴力解法很简单,用结构体把每个点的x、y坐标都存下来之后,用一个二重循环即可获得距离的最大值。但是这样做的时间复杂度也达到了O(n的平方)

Laok0413的博客 9655

强连通分量及缩点tarjan算法解析

强连通分量: 简言之 就是找环(每条边只走一次,两两可达) 孤立的一个点也是一个连通分量   使用tarjan算法 在嵌套的多个环中优先得到最大环( 最小环就是每个孤立点)   定义: int Time, DFN[N], Low[N]; DFN[i]表示 遍历到 i 点时是第几次dfs Low[u] 表示 以u点为父节点的 子树 能连接到 [栈中] 最上端的点   int

九野的博客 72万+

STM32按键中断切换红绿蓝灯

通过以上步骤,我们实现了使用STM32的按键中断来切换红绿蓝LED灯的功能。在实际应用中,可能还需要考虑更多的细节,如按键消抖、更精确的延时实现等。但上述代码提供了一个基本的框架和思路,可以根据具体需求进行扩展和优化。希望这篇博客对您有所帮助!

2301_79865940的博客 952

强连通分量(Tarjan算法)

强连通分量 有向图强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量。 Tarjan 算法是基于对图优先搜素的算法 , 每个强连通分量为搜索树中的一棵子树...

森宝的博客 8235

SOC-ATF 安全启动BL2流程分析(2)

Bl2的启动流程与bl1类似,主要区别是bl2的初始化流程比bl1更简单,但其可能需要加载更多的镜像,如bl31、bl32和bl33。由于bl2可以运行于el3或s-el1,它们的入口函数和处理流程都有所区别,为了简化分析我们选取比较常见的s-el1方式,以下为其总体执行流程:上图仅供参考,实际看一下代码结构:上面截图展示了 链接汇编文件,bl2_el3.ld.S 是指bl2 运行于el3;bl2.ld.S 是实现bl2运行于s-el1 .

z20230508的博客 1387

OpenClaw插件配置错误修复完全指南

插件文件名是插件目录结构正确package.json配置正确OpenClaw的插件系统还在快速迭代,偶尔会遇到这种配置问题。看错误信息检查配置文件检查实际文件对比差异修复问题大部分问题都能解决。查看官方文档:https://openclaw.ai/docs提Issue:https://github.com/openclaw/openclaw/issues加入Discord社区求助快速修复命令汇总。

Maynor的博客 4092

Tarjan算法——求强连通分量

什么是强连通分量 强连通分量,是一个有向图的最大强连通子图(看起来好像没有什么解释效果…),好吧,强连通分量就是在一个有向图中,从任意一个点出发,最多可以走过的所有的点构成的一个点集,将其称之为强连通。强连通分量就是这些集合里面最大的那个。如下图: 2,3,5,就是强连通的,因为从二出发可以到达2,3,5的任意一个点。所以1,4,也是强连通的。我们不难发现,除了双向边以外,强连通都是环套环的形式...

Martisum的博客 3511

【Unity实战篇 】| Unity实现 文本框可以自适应大小,到达最大宽度之后再缩小字体

Unity实现 Text文本框可以自适应大小,到达最大宽度之后再缩小字体 需求:在文本框可以自适应大小拉伸的前提下,增加一个最大限制宽度,使其到达最大宽度后 再启用 Best Fit 实现自适应改变文字大小以适应文本框的大小。 在Unity中经常会用到文本组件的自适应大小功能,一般使用HorizontalLayoutGroup和ContentSizeFitter组件配合即可实现文本自适应的效果。

努力前行,总会成为自己心中的那道光 3万+

SCC-Tarjan算法,强连通分量算法,从dfs到Tarjan详解

如何一步步从dfs到tarjan算法?

EQUINOX1的博客 2640

Linux(31)Rockchip RK3568 Ubuntu22.04上部署 Docker: 问题与解决方案

本博客在总结这些问题及其解决方案,为遇到类似问题的兄弟提供参考,如果我后续有碰到其他Docker 问题也会在此更新补充。

我其实什么都不会 1645

2022年全国职业院校技能大赛高职组大数据技术与应用赛项赛卷⑩—解析答案

工业互联网是工业全要素、全产业链、全价值链的全面连接,是人、机、物、工厂互联互通的新型工业生产制造服务体系,是互联网从消费领域向生产领域、从虚拟经济向实体经济拓展的核心载体,是建设现代化经济体系、实现高质量发展和塑造全球产业竞争力的关键支撑。加快发展工业互联网产业,不仅是各国顺应产业发展大势,抢占产业未来制高点的战略选择,也是我国推动制造业质量变革、效率变革和动力变革,实现高质量发展的客观要求。 为完成工业大数据分析工作,你所在的小组将应用大数据技术,以Scala作为整个项目的基础开发语言,基于大数据平台综

我是网络安全兼技能大赛工程师,专注网络安全技术学习与技能大赛实战!持续分享最新的技术文章,帮你少走弯路,一起在技术赛道上进步~ 581

两种强连通分量算法——tarjan和kosaraju

这两个算法其实都非常简单,其中tarjan算法只需进行一次dfs,更为高效。但我更喜欢kosaraju算法,因为这个算法的思考过程更加巧妙。 tarjan算法的思想非常简单直观,随便找篇教程就很容易理解,下面我主要讲讲kosaraju算法。 kosaraju算法进行两次dfs,第一次在原图上进行,并在结点递归调用返回时将结点压入一个栈中,第二次dfs在原图的逆图上进行,并且初始点选择栈中最上面

cgjiayou 6093

手把手教你学Simulink--四翼无人机滑模控制器设计与仿真

四翼无人机(Quadcopter)滑模控制(Sliding Mode Control, SMC)是一种鲁棒性很强的非线性控制方法,特别适用于存在模型不确定性和外部扰动的情况。滑模控制器通过强制系统状态进入并保持在一个预定义的滑模面上,从而实现对期望轨迹或姿态的精确跟踪。在这个教程中,我们将使用MATLAB和Simulink来设计并仿真一个基于滑模控制策略的四翼无人机系统。

MHD0815的博客 184

强连通分量个数的tarjan算法

/* 强连通分量个数tarjan算法 */ #include #include #include #include using namespace std; const int maxV=1000,maxE=1000000; struct Edge { int to,next; }edge[maxE]; int Adj[maxV],Size; void ini

码代码的猿猿 1604

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

强连通分量Tarjan算法

说到以Tarjan命名的算法,我们经常提到的有3个,其中就包括本文所介绍的求强连通分量的Tarjan算法。而提出此算法的普林斯顿大学的Robert E Tarjan教授也是1986年的图灵奖获得者(具体原因请看本博“历届图灵奖得主”一文)。         首先明确几个概念。 强连通图。在一个强连通图中,任意两个点都通过一定路径互相连通。比如图一是一个强连通图,而图二不是。因为没

不可不戒 1349

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

[图解算法]Tarjan算法求图的强连通分量

强连通分量简介   有向图强连通分量:在有向图G中,如果两个顶点Vi,VjV_i, V_jVi​,Vj​ 间有一条从ViV_iVi​到VjV_jVj​的有向路径,同时还有一条从VjV_jVj​到ViV_iVi​的有向路径,则称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量(strongly con...

飞鱼の博客 957

tarjan算法求解强连通分量问题

SCC

weixin_61819021的博客 543

强连通分量:Tarjan算法的完整推导

强连通分量(SCC)是有向图中的重要概念。Tarjan算法用一次DFS就能找出所有强连通分量,算法精妙至极。很多人只记住代码,却不知道dfn和low的含义、为什么这样能找到SCC。我并没有能力让你成为图论专家,我只是想让你彻底理解强连通分量的定义、Tarjan算法的DFS时间戳原理、以及low值的更新规则。每个细节都会详细推导,讲透本质。从"关键连接"问题出发,剖析Tarjan算法的核心思想与数学原理。

比较是偷走幸福的小偷 4241

强连通分量 Tarjan 算法 学习笔记

Part1. 强连通分量 强连通 我们知道,在无向图中,如果点 uuu 和点 vvv 直接或间接可以相互到达对方,就说点 uuu,vvv 连通。 #mermaid-svg-zsspkck5SSk03GUE .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-zsspkck5SSk03GUE .label text{f

TMJYH09's csdn blog 414

强连通分量之tarjan算法

强连通分量问题通常可归纳为要求出强连通分量,然后通过缩点(将得出的每个连通分量视为一个点,然后继续构图,例如连通分量A有一个点有一条边指向连通分量B的一个点,那么在A上搭一条边到B,其他连通分量也以此类推)。求图的强连通分量一个算法为tarjan,在http://www.byvoid.com/blog/scc-tarjan/这博客中对tarjan的算法描述得非常的详细。我这里转载方便自己以后看[有向图强连通分量]在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connect

黄廉政的专栏 8730
上一篇: 动态开点线段树
下一篇: 一些奇怪的图
Wu_while
博客等级 码龄6年 50粉丝 53原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值