【图论】虚树 - 模板总结

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

/********************* 虚树 *********************/
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;
/***********************************************/

你可能感兴趣的:(图论,图论)