【图论】虚树 - 模板总结

适用于解决一棵树中只需要用到少部分点的时候,将需要用到的点提出来单独建一棵树

/********************* 虚树 *********************/
struct edge
{
	int to, next;
	int val;
};

struct Virtual_Tree
{
	int n; // 点数
	int dfn[N]; // dfs序
	int dep[N]; // 深度
	int fa[N][25], m[N];
	int num; // 关键点数
	vector<int> lst; // 关键点
	bool query[N]; // 是否为关键点
	int top, cnt1 = 1, cnt2 = 1, dfscnt = 1;
	int stk[MAXN];
	int head1[N], head2[N];
	struct edge edge1[N << 1], edge2[N << 1]; // 原树和虚树

	/* 在下方添加需要的信息定义 */
	int minv[N];
	/***************************/

	// 初始化
	void init()
	{
		for (int i = 1; i <= n; i ++ )
		{
			dfn[i] = dep[i] = m[i] = query[i] = 0;
			for (int j = 0; j < 24; j ++ ) fa[i][j] = 0;
		}
	}

	// 原树建边
	void add1(int x, int y, int v)
	{
		edge1[cnt1].next = head1[x];
    	edge1[cnt1].to = y;
    	edge1[cnt1].val = v;
    	head1[x] = cnt1 ++ ;
	}

	// 虚树建边
	void add2(int x, int y)
	{
    	edge2[cnt2].next = head2[x];
    	edge2[cnt2].to = y;
    	head2[x] = cnt2 ++ ;
	}

	// 预处理原树基本信息
	void dfs1(int pos)
	{
    	int k;
    	for (k = 0; fa[pos][k]; k ++ ) fa[pos][k + 1] = fa[fa[pos][k]][k];
    	m[pos] = k;
    	dfn[pos] = dfscnt++;
    	for (int i = head1[pos]; i; i = edge1[i].next)
    	{
    	    int to = edge1[i].to;
    	    if (!dfn[to])
    	    {
    	        dep[to] = dep[pos] + 1;
    	        fa[to][0] = pos;
				/* 在下方处理需要的信息 */
    	        minv[to] = min(minv[pos], edge1[i].val);
				/***********************/
    	        dfs1(to);
    	    }
    	}
	}

	// 倍增求lca
	int lca(int x, int y)
	{
    	if (dep[x] < dep[y]) swap(x, y);
		for (int i = m[x]; i > -1; i -- )
		{
    		if (dep[fa[x][i]] >= dep[y]) x = fa[x][i];
		}
    	if (x == y) return x;
		for (int i = m[x]; i > -1; i -- )
		{
    		if (fa[x][i] != fa[y][i])
    		{
    		    x = fa[x][i];
    		    y = fa[y][i];
    		}
		}
    	return fa[x][0];
	}

	// 建虚树 关键点存在lst里 lst大小为k 下标从0开始
	void build(int k, vector<int>& lst)
	{
		// 按照dfs序排序规则
		auto cmp = [&](int x1, int x2)
		{
			return dfn[x1] < dfn[x2];
		};
		sort(lst.begin(), lst.end(), cmp);
		stk[top = 1] = lst[0];
		for (int i = 1; i < k; i ++ )
		{
			int now = lst[i];
			int lc = lca(now, stk[top]);
			while (1)
			{
				if (dep[lc] >= dep[stk[top - 1]])
                {
                    if (lc != stk[top])
                    {
                        add2(lc, stk[top]);
                        if (lc != stk[top - 1]) stk[top] = lc;
                        else top -- ;
                    }
                    break;
                }
                else
                {
                    add2(stk[top - 1], stk[top]);
                    top -- ;
                }
			}
			stk[++ top] = now;
		}
		while (-- top) add2(stk[top], stk[top + 1]);
	}

	// 树形dp计算答案
	int dfs2(int u)
	{
		/* 下方填写计算答案代码逻辑 */

		/**************************/

		// 清空虚树
		query[u] = false;
		head2[u] = 0;
		return res;
	}

	// 在下方填写解题逻辑
	void solve()
	{
		/* 思路 */

		/********/

		// 每次建虚树后需要清空
		cnt2 = 1;
		lst.clear();
	}
} vtr;
/***********************************************/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Texcavator

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值