• luogu3384 【模板】 树链剖分


    题目大意

    已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作:
    操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z
    操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和
    操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z
    操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和

    基本概念

    对路径和子树上的值进行整体操作,我们要用线段树。每个节点有个Id,对应线段树维护的区间(以后简称区间)上的点。Id要满足以下条件:

    1. 所有子树上的节点的Id构成一段连续的区间。
    2. 每个节点都属于且只属于一个重链,使得重链上的节点的Id构成一段连续的区间。

    对于一个节点u,它的子节点v属于u所在重链当且仅当v是u的孩子中size(子树中元素的个数)最大的。此时v叫做u的重孩子。

    连接两个属于不同重链的节点的是轻边,这两个节点的Id之差必大于1。

    实现方法

    预处理

    先Dfs1求出每个节点的Size,深度,并通过Size求出每个节点的重儿子;再Dfs2求出重链(具体表示为每个节点所在链的链头),并按照Dfs序设置每个节点的Id和LastSonId(Dfs到的子树中的最后一个节点的Id)。

    子树操作

    直接用线段树操作当前节点的Id和LastSonId即可。

    路径操作

    当两个节点u,v所在重链的链头不同时,令u为所在链头深度较深的节点,用线段树对u的链头的Id和u的Id进行操作,然后u通过与u链头相连的轻边移动到u链头的父亲那里去,如此循环。最后,当u,v所在链头相同时,令u为深度较深的节点,用线段树对v的Id和u的Id操作即可。

    注意事项

    • 边的数量应当是节点数量的二倍,因为u到v一条边,v到u也一条边。
    • 令u为所在链头深度较深的节点,而不是令u为深度较深的节点。
    • 路径操作对uv链头不同时的循环完后,即使u==v,也要线段树操作。
    • 由于题目中要求取模,所以struct SplitTree中没有一个+号。
    #include <cstdio>
    #include <cstring>
    #include <cassert>
    #include <algorithm>
    using namespace std;
    
    const int MAX_NODE = 100010, MAX_EDGE = MAX_NODE*2, MAX_RANGE_NODE = MAX_NODE * 10;
    int P;
    #define LOOP(i, n) for(int i=1; i<=n; i++)
    
    struct SplitTree
    {
    private:
    #define ModPlus(x, y) ((x)+((y)%P))%P
    
    	struct Node;
    	struct Edge;
    
    	struct Node
    	{
    		Node *HeavySon, *Top, *Father;
    		Edge *Head;
    		int Size, Id, LastSonId, Weight, Depth;
    	}_nodes[MAX_NODE], *Root;
    
    	struct Edge
    	{
    		Edge *Next;
    		Node *From, *To;
    	}*_edges[MAX_EDGE];
    
    	int _lastId, _edgeCnt;
    
    	struct RangeTree
    	{
    	private:
    		int Sum[MAX_RANGE_NODE], PlusTag[MAX_RANGE_NODE];
    		int TotRange;
    
    		void PushDown(int cur, int sl, int sr)
    		{
    			if (PlusTag[cur])
    			{
    				int mid = (sl + sr) / 2;
    				PlusTag[cur * 2] = ModPlus(PlusTag[cur * 2], PlusTag[cur]);
    				PlusTag[cur * 2 + 1] = ModPlus(PlusTag[cur * 2 + 1], PlusTag[cur]);
    				Sum[cur * 2] = ModPlus(Sum[cur * 2], PlusTag[cur] * (mid - sl + 1));
    				Sum[cur * 2 + 1] = ModPlus(Sum[cur * 2 + 1], PlusTag[cur] * (sr - mid));
    				PlusTag[cur] = 0;
    			}
    		}
    
    		void PullUp(int cur)
    		{
    			Sum[cur] = ModPlus(Sum[cur * 2], Sum[cur * 2 + 1]);
    		}
    
    		void Update(int cur, int sl, int sr, int al, int ar, int value)
    		{
    			if (al <= sl && sr <= ar)
    			{
    				Sum[cur] = ModPlus(Sum[cur], (sr - sl + 1)*value);
    				PlusTag[cur] = ModPlus(PlusTag[cur], value);
    				return;
    			}
    			PushDown(cur, sl, sr);
    			int mid = (sl + sr) / 2;
    			if (al <= mid)
    				Update(cur * 2, sl, mid, al, ar, value);
    			if (ar > mid)
    				Update(cur * 2 + 1, mid + 1, sr, al, ar, value);
    			PullUp(cur);
    		}
    
    		int Query(int cur, int sl, int sr, int al, int ar)
    		{
    			if (al <= sl&&sr <= ar)
    				return Sum[cur];
    			PushDown(cur, sl, sr);
    			int mid = (sl + sr) / 2, ans = 0;
    			if (al <= mid)
    				ans = ModPlus(ans, Query(cur * 2, sl, mid, al, ar));
    			if (ar > mid)
    				ans = ModPlus(ans, Query(cur * 2 + 1, mid + 1, sr, al, ar));
    			PullUp(cur);
    			return ans;
    		}
    
    	public:
    		void Update(int l, int r, int value)
    		{
    			Update(1, 1, TotRange, l, r, value);
    		}
    
    		int Query(int l, int r)
    		{
    			return Query(1, 1, TotRange, l, r);
    		}
    
    		void Init(int totRange)
    		{
    			memset(Sum, 0, sizeof(Sum));
    			memset(PlusTag, 0, sizeof(PlusTag));
    			TotRange = totRange;
    		}
    	}r;
    
    	Edge *NewEdge()
    	{
    		return _edges[++_edgeCnt] = new Edge();
    	}
    
    	void AddEdge(Node *from, Node *to)
    	{
    		Edge *e = NewEdge();
    		e->From = from;
    		e->To = to;
    		e->Next = e->From->Head;
    		e->From->Head = e;
    	}
    
    	void Dfs1(Node *cur, Node *father, int depth)
    	{
    		cur->Size = 1;
    		cur->Depth = depth;
    		cur->Father = father;
    		int maxSonSize = 0;
    		for (Edge *e = cur->Head; e; e = e->Next)
    		{
    			if (e->To != father)
    			{
    				Dfs1(e->To, cur, depth + 1);
    				cur->Size += e->To->Size;
    				if (e->To->Size > maxSonSize)
    				{
    					maxSonSize = e->To->Size;
    					cur->HeavySon = e->To;
    				}
    			}
    		}
    	}
    
    	void Dfs2(Node *cur, Node *top)
    	{
    		cur->Top = top;
    		cur->Id = ++_lastId;
    		r.Update(cur->Id, cur->Id, cur->Weight);
    		if (cur->HeavySon)
    			Dfs2(cur->HeavySon, top);
    		for (Edge *e = cur->Head; e; e = e->Next)
    			if (e->To != cur->HeavySon && e->To != cur->Father)
    				Dfs2(e->To, e->To);
    		cur->LastSonId = _lastId;
    	}
    
    	void UpdatePath(Node *u, Node *v, int value)
    	{
    		while (u->Top != v->Top)
    		{
    			if (u->Top->Depth < v->Top->Depth)
    				swap(u, v);
    			r.Update(u->Top->Id, u->Id, value);
    			u = u->Top->Father;
    		}
    		if (u->Depth < v->Depth)
    			swap(u, v);
    		r.Update(v->Id, u->Id, value);
    	}
    
    	int QueryPath(Node *u, Node *v)
    	{
    		int sum = 0;
    		while (u->Top != v->Top)
    		{
    			if (u->Top->Depth < v->Top->Depth)
    				swap(u, v);
    			sum = ModPlus(sum, r.Query(u->Top->Id, u->Id));
    			u = u->Top->Father;
    		}
    		if (u->Depth < v->Depth)
    			swap(u, v);
    		sum = ModPlus(sum, r.Query(v->Id, u->Id));
    		return sum;
    	}
    
    	void UpdateSubTree(Node *cur, int value)
    	{
    		r.Update(cur->Id, cur->LastSonId, value);
    	}
    
    	int QuerySubTree(Node *cur)
    	{
    		return r.Query(cur->Id, cur->LastSonId);
    	}
    
    public:
    	SplitTree(int root, int totNode)
    	{
    		memset(_nodes, 0, sizeof(_nodes));
    		memset(_edges, 0, sizeof(_edges));
    		_lastId = _edgeCnt = 0;
    		Root = _nodes + root;
    		r.Init(totNode);
    	}
    
    	void SetNodeWeight(int id, int w)
    	{
    		_nodes[id].Weight = w;
    	}
    
    	void Build(int u, int v)
    	{
    		AddEdge(_nodes + u, _nodes + v);
    		AddEdge(_nodes + v, _nodes + u);
    	}
    
    	void Init()
    	{
    		Dfs1(Root, NULL, 1);
    		Dfs2(Root, Root);
    	}
    
    	void UpdatePath(int u, int v, int value)
    	{
    		UpdatePath(_nodes + u, _nodes + v, value);
    	}
    
    	int QueryPath(int u, int v)
    	{
    		return QueryPath(_nodes + u, _nodes + v);
    	}
    
    	void UpdateSubTree(int u, int value)
    	{
    		UpdateSubTree(_nodes + u, value);
    	}
    
    	int QuerySubTree(int u)
    	{
    		return QuerySubTree(_nodes + u);
    	}
    };
    
    int main()
    {
    	int totNode, rootId, opCnt, w, u, v, op, val;
    	scanf("%d%d%d%d", &totNode, &opCnt, &rootId, &P);
    	static SplitTree g(rootId, totNode);
    	LOOP(i, totNode)
    	{
    		scanf("%d", &w);
    		g.SetNodeWeight(i, w);
    	}
    	LOOP(i, totNode - 1)
    	{
    		scanf("%d%d", &u, &v);
    		g.Build(u, v);
    	}
    	g.Init();
    	while (opCnt--)
    	{
    		scanf("%d", &op);
    		switch (op)
    		{
    		case 1://UpdatePath
    			scanf("%d%d%d", &u, &v, &val);
    			g.UpdatePath(u, v, val);
    			break;
    		case 2://QueryPath
    			scanf("%d%d", &u, &v);
    			printf("%d
    ", g.QueryPath(u, v));
    			break;
    		case 3://UpdateSubTree
    			scanf("%d%d", &u, &val);
    			g.UpdateSubTree(u, val);
    			break;
    		case 4://QuerySubTree
    			scanf("%d", &u);
    			printf("%d
    ", g.QuerySubTree(u));
    			//printf("100
    ");
    			break;
    		}
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    【算法学习笔记】27.动态规划 解题报告 SJTU OJ 1254 传手绢
    【算法学习笔记】26.扫描维护法 解题报告 SJTU OJ 1133 数星星
    【算法学习笔记】25.贪心法 均分纸牌问题的分析
    【算法学习笔记】24.记忆化搜索 解题报告 SJTU OJ 1002 二哥种花生
    【算法学习笔记】23.动态规划 解题报告 SJTU OJ 1280 整装待发
    【算法学习笔记】22.算法设计初步 二分查找 上下界判断
    【算法学习笔记】21.算法设计初步 求第k个数 划分法 快排法
    【算法学习笔记】20.算法设计初步 归并排序 求逆序数
    【算法学习笔记】19.算法设计初步 最大子列和问题的几种方法
    【算法学习笔记】18.暴力求解法06 隐式图搜索2 八数码问题 未启发
  • 原文地址:https://www.cnblogs.com/headboy2002/p/8654139.html
Copyright © 2020-2023  润新知