题目大意

已知一棵包含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\n", 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\n", g.QuerySubTree(u));
//printf("100\n");
break;
}
}
return 0;
}

  

luogu3384 【模板】 树链剖分的更多相关文章

  1. [luogu P3384] [模板]树链剖分

    [luogu P3384] [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点 ...

  2. luoguP3384 [模板]树链剖分

    luogu P3384 [模板]树链剖分 题目 #include<iostream> #include<cstdlib> #include<cstdio> #inc ...

  3. [洛谷P3384] [模板] 树链剖分

    题目传送门 显然是一道模板题. 然而索引出现了错误,狂wa不止. 感谢神犇Dr_J指正.%%%orz. 建线段树的时候,第44行. 把sum[p]=bv[pos[l]]%mod;打成了sum[p]=b ...

  4. 模板 树链剖分BFS版本

    //点和线段树都从1开始 //边使用vector vector<int> G[maxn]; ],num[maxn],iii[maxn],b[maxn],a[maxn],top[maxn], ...

  5. P3384 [模板] 树链剖分

    #include <bits/stdc++.h> using namespace std; typedef long long ll; int n, m, rt, mod, cnt, to ...

  6. 树链剖分详解(洛谷模板 P3384)

    洛谷·[模板]树链剖分 写在前面 首先,在学树链剖分之前最好先把 LCA.树形DP.DFS序 这三个知识点学了 emm还有必备的 链式前向星.线段树 也要先学了. 如果这三个知识点没掌握好的话,树链剖 ...

  7. 『题解』洛谷P3384 【模板】树链剖分

    Problem Portal Portal1: Luogu Description 如题,已知一棵包含\(N\)个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作\(1\): ...

  8. luogu3384 【模板】树链剖分

    P3384 [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节 ...

  9. luogu3384 /// 树链剖分+线段树模板

    题目大意: https://www.luogu.org/problemnew/show/P3384 树链剖分的讲解 两个dfs() 修改 查询 很详细很好理解 https://www.cnblogs. ...

  10. P3384 【模板】树链剖分

    P3384 [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节 ...

随机推荐

  1. JCaptcha+Memcache的验证码集群实现

    一.问题背景 为了防止垃圾信息发布机器人的自动提交攻击,采用CAPTCHA验证码来保护该模块,提高攻击者的成本. 二.验证码简介 全自动区分计算机和人类的图灵测试(Completely Automat ...

  2. 洛谷 [P2148] E&G

    SG函数的应用 首先每一组都是独立的,所以我们可以求出每一组的SG值异或出来. 那么怎么求每一组的SG值呢,网上的题解都是打表找规律,但其实这个规律是可以证明的 先看规律: x为奇数,y为奇数:SG= ...

  3. 安装破解版的webstorne

    参考以下链接:https://www.cnblogs.com/cui-cui/p/8507435.html

  4. 在C#中使用Json.Net进行序列化和反序列化及定制化

    序列化(Serialize)是将对象转换成字节流,并将其用于存储或传输的过程,主要用途是保存对象的状态,以便在需要时重新创建该对象:反序列化(Deserialize)则是将上面的字节流转换为相应对象的 ...

  5. Java并发编程,深入理解ReentrantLock

    ReentrantLock简介 ReentrantLock重入锁,是实现Lock接口的一个类,也是在实际编程中使用频率很高的一个锁, 支持重入性,表示能够对共享资源能够重复加锁,即当前线程获取该锁再次 ...

  6. iOS 读书笔记 第一章

    1.确定某个实例或类方法是否可用. 1)使用NSObject的类方法instancesRespondToSelector:来确定是否在该类的一个实例中存在一个特定的选择器. NSArray *arra ...

  7. Linux 开发板网络设置

    改动IP地址步骤: ①改动/etc/eth0-setting 命令:vi /etc/eth0-setting ②改动对应的信息.最后:wq退出 ③重新启动eth0 命令:/etc/init.d/ifc ...

  8. BUPT复试专题—众数(2014)

    题目描述 有一个长度为N的非降数列,求数列中出现最多的数,若答案不唯一输出最小的数 输入 第一行T表示测试数据的组数(T<100) 对于每组测试数据: 第一行是一个正整数N表示数列长度 第二行有 ...

  9. UITabBarController ---- 标签视图控制器

    直接上代码: // // AppDelegate.m // // #import "AppDelegate.h" #import "RootViewController. ...

  10. Java基础 笔记(四)

    泛型: 泛型:通过<数据额类型>来接受一种引用数据类型.作用在编译时.使用检查 加入集合的对象是否属于该类型,从而把执行时的问题转换为编译时异常.提高程序的安全性 避免抛出 ClassCa ...