zoukankan      html  css  js  c++  java
  • 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;
    }
    

      

  • 相关阅读:
    在Win7 x64环境中将World Wind Java SDK 2.1.0嵌入到Eclipse中的方法
    WW中文地名标注:输出*.wwp和*.wpl文件
    [转]Microsoft Robotics Studio:微软仿真机器人集成开发环境,简称MSRS
    C#中定义类时关于CLSCompliant属性的声明
    Android Studio中使用Java+OpenGL ES创建Android项目
    [转]使用Unity进行3D开发的思路和主要技术优势
    在C++中实现委托事件的方法
    VS2008新建MFC程序时提示:当前页面的脚本发送错误 不是有效的Win32应用程序的解决办法
    [Web 前端] mockjs让前端开发独立于后端
    [Web 前端] 如何构建React+Mobx+Superagent的完整框架
  • 原文地址:https://www.cnblogs.com/headboy2002/p/8654139.html
Copyright © 2011-2022 走看看