51nod 1462 树据结构 | 树链剖分 矩阵乘法
题目链接
题目描述
给一颗以1为根的树。
每个点有两个权值:vi, ti,一开始全部是零。
Q次操作:
读入o, u, d
o = 1 对u到根上所有点的vi += d
o = 2 对u到根上所有点的ti += vi * d
最后,输出每个点的ti值(n, Q <= 100000)
有50%的数据N,Q <= 10000
注:所有数64位整数不会爆。
题解
这道题好神奇啊……看讨论版里的 AntiLeaf 大神的矩阵乘法打标记才找到思路,然后又看到 ccz181078 的评论,发现常数可以优化到这么小……真是太神了!一月份听尹涵学姐提到过矩阵乘法结合线段树,今天终于做到这样的题了2333
好的说题解。
可以发现(我并没发现),题中的两种操作都可以用矩阵来表示。假如用下面这个矩阵表示一个节点的状态
\]
那么操作1可以表示为
\]
(可以看出那个1就是用来给v加上d的)
然后操作2可以表示为
\]
这样两种操作就都可以通过矩阵来做啦。
然后根据题目要求显然需要树链剖分,线段树上维护矩阵就可以了。矩阵乘法满足结合律,所以可以正常地下放lazy标记。
但是这个矩阵乘法常数比较大(矩阵乘法\(O(n^3)\),\(n^3 = 27\))。但是这个矩阵比较特殊,它左下角三个位置恒为0,主对角线恒为1,于是并不需要\(O(n^3)\)地做矩阵乘法,实际上只需要4次加法和1次乘法,这是一个显著的常数优化!
代码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <vector>
#define space putchar(' ')
#define enter putchar('\n')
typedef long long ll;
using namespace std;
template <class T>
void read(T &x){
char c;
bool op = 0;
while(c = getchar(), c < '0' || c > '9')
if(c == '-') op = 1;
x = c - '0';
while(c = getchar(), c >= '0' && c <= '9')
x = x * 10 + c - '0';
if(op) x = -x;
}
template <class T>
void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x >= 10) write(x / 10);
putchar('0' + x % 10);
}
const int N = 100005;
int n, m, adj[N], nxt[N];
int fa[N], son[N], sze[N], top[N], pos[N], idx[N], tot;
ll ans[N];
struct matrix {
ll g[3][3];
matrix(){
memset(g, 0, sizeof(g));
}
matrix(int x){
memset(g, 0, sizeof(g));
for(int i = 0; i < 3; i++)
g[i][i] = 1;
}
bool empty(){
return !g[0][1] && !g[0][2] && !g[1][2];
}
void clear(){
g[0][1] = g[0][2] = g[1][2] = 0;
}
friend matrix opt_multi(const matrix &a, const matrix &b){
matrix c(1);
c.g[0][1] = a.g[0][1] + b.g[0][1];
c.g[1][2] = a.g[1][2] + b.g[1][2];
c.g[0][2] = a.g[0][2] + b.g[0][2] + a.g[0][1] * b.g[1][2];
return c;
}
} lazy[4*N];
void pushdown(int k){
lazy[k << 1] = opt_multi(lazy[k << 1], lazy[k]);
lazy[k << 1 | 1] = opt_multi(lazy[k << 1 | 1], lazy[k]);
lazy[k].clear();
}
void change(int k, int l, int r, int ql, int qr, const matrix &x){
if(ql <= l && qr >= r) return (void)(lazy[k] = opt_multi(lazy[k], x));
if(!lazy[k].empty()) pushdown(k);
int mid = (l + r) >> 1;
if(ql <= mid) change(k << 1, l, mid, ql, qr, x);
if(qr > mid) change(k << 1 | 1, mid + 1, r, ql, qr, x);
}
void pushdown_all(int k, int l, int r){
if(l == r) return (void)(ans[idx[l]] = lazy[k].g[0][2]);
if(!lazy[k].empty()) pushdown(k);
int mid = (l + r) >> 1;
pushdown_all(k << 1, l, mid);
pushdown_all(k << 1 | 1, mid + 1, r);
}
void add(int u, int v){
nxt[v] = adj[u];
adj[u] = v;
}
void bfs(){
static int que[N], qr;
que[qr = 1] = 1;
for(int ql = 1; ql <= qr; ql++)
for(int u = que[ql], v = adj[u]; v; v = nxt[v])
que[++qr] = v;
for(int ql = qr, u; ql; ql--){
u = que[ql];
sze[fa[u]] += ++sze[u];
if(sze[u] > sze[son[fa[u]]]) son[fa[u]] = u;
}
for(int ql = 1, u; ql <= qr; ql++)
if(!top[u = que[ql]])
for(int v = u; v; v = son[v])
top[v] = u, idx[pos[v] = ++tot] = v;
}
void path_change(int o, int u, ll d){
matrix x(1);
(o == 1 ? x.g[0][1] : x.g[1][2]) = d;
while(u){
change(1, 1, n, pos[top[u]], pos[u], x);
u = fa[top[u]];
}
}
int main(){
read(n);
for(int i = 2; i <= n; i++)
read(fa[i]), add(fa[i], i);
bfs();
read(m);
while(m--){
int o, u;
ll d;
read(o), read(u), read(d);
path_change(o, u, d);
}
pushdown_all(1, 1, n);
for(int i = 1; i <= n; i++)
write(ans[i]), enter;
return 0;
}
51nod 1462 树据结构 | 树链剖分 矩阵乘法的更多相关文章
- luoguP4719 【模板】动态 DP 线段树+树链剖分+矩阵乘法+动态DP
题目描述 给定一棵n个点的树,点带点权. 有m次操作,每次操作给定x,y,表示修改点x的权值为y. 你需要在每次操作之后求出这棵树的最大权独立集的权值大小. 输入输出格式 输入格式: 第一行,n,m分 ...
- 树的统计Count---树链剖分
NEFU专项训练十和十一——树链剖分 Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t ...
- [LOJ3014][JOI 2019 Final]独特的城市——树的直径+长链剖分
题目链接: [JOI 2019 Final]独特的城市 对于每个点,它的答案最大就是与它距离最远的点的距离. 而如果与它距离为$x$的点有大于等于两个,那么与它距离小于等于$x$的点都不会被计入答案. ...
- BZOJ 1036: [ZJOI2008]树的统计Count-树链剖分(点权)(单点更新、路径节点最值、路径求和)模板,超级认真写了注释啊啊啊
1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 23015 Solved: 9336[Submit ...
- 6.3 省选模拟赛 Decompose 动态dp 树链剖分 set
LINK:Decompose 看起来很难 实际上也很难 考验选手的dp 树链剖分 矩阵乘法的能力. 容易列出dp方程 暴力dp 期望得分28. 对于链的情况 容易发现dp方程可以转矩阵乘法 然后利用线 ...
- LOJ3053 十二省联考2019 希望 容斥、树形DP、长链剖分
传送门 官方题解其实讲的挺清楚了,就是锅有点多-- 一些有启发性的部分分 L=N 一个经典(反正我是不会)的容斥:最后的答案=对于每个点能够以它作为集合点的方案数-对于每条边能够以其两个端点作为集合点 ...
- 219.01.19 bzoj3252: 攻略(长链剖分+贪心)
传送门 长链剖分好题. 题意:给一棵带点权的树,可以从根节点到任一叶节点走kkk次,走过的点只能计算一次,问kkk次走过的点点权值和最大值. 思路: 考虑将整棵树带权长链剖分,这样链与链之间是不会重复 ...
- CF718C Sasha and Array 线段树 + 矩阵乘法
有两个操作: 将 $[l,r]$所有数 + $x$ 求 $\sum_{i=l}^{r}fib(i)$ $n=m=10^5$ 直接求不好求,改成矩阵乘法的形式: $a_{i}=M^x\times ...
- 51Nod 1199 Money out of Thin Air (树链剖分+线段树)
1199 Money out of Thin Air 题目来源: Ural 基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 收藏 关注 一棵有N个节点的树,每 ...
随机推荐
- dpkg打包与解包
1.打包 dpkg -b 2.解包 2.1 dpkg -X 解出包内容 2.2 dpkg -e 输出包控制信息
- 在平衡树的海洋中畅游(一)——Treap
记得有一天翔哥毒奶我们: 当你们已经在平衡树的海洋中畅游时,我还在线段树的泥沼中挣扎. 我觉得其实像我这种对平衡树一无所知的蒟蒻也要开一开数据结构了. 然后花了一天啃了下最简单的平衡树Treap,感觉 ...
- 学习ML.NET(1): 构建流水线
ML.NET使用LearningPipeline类定义执行期望的机器学习任务所需的步骤,让机器学习的流程变得直观. 下面用鸢尾花瓣预测快速入门的示例代码讲解流水线是如何工作的. using Micro ...
- 聊聊Zookeeper应用场景、架构设计、选主机制
Zookeeper作为一个分布式协调系统提供了一项基本服务:分布式锁服务,分布式锁是分布式协调技术实现的核心内容.像配置管理.任务分发.组服务.分布式消息队列.分布式通知/协调等,这些应用实际上都是基 ...
- ABP+AdminLTE+Bootstrap Table权限管理系统第九节--AdminLTE引入及模板页和布局和菜单
返回总目录:ABP+AdminLTE+Bootstrap Table权限管理系统一期 AdminLTE AdminLTE 官网地址:https://adminlte.io/themes/AdminLT ...
- WinForm 简易仿360界面控件
因为经常要做一些1.2千行的小工具,WinForm自带的TabCtrl又不美观,所以想做成360的样子,在网上找来找去,都只有散乱的代码,没有可以通用的结构,于是自己写了一个简易的通用控件. 控件主要 ...
- Websocket(一)——原理及基本属性和方法
初次接触 WebSocket 的人,都会问同样的问题:我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处? 答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起. 举例 ...
- 浏览器跨域请求之credentials
-时间起源- 前段时间,需要弄个简单的网站出来,访问远程的api服务. 我是这么做的.首先是在搭建一个nodejs服务来运行前端页面.在我请求登录的时候,能成功返回相应的成功信息.然后,当我再次请求读 ...
- combox的基本应用
easyui-combox:控件的初始化: 可以在其中进行文字的筛选功能(过滤), 动态加载数据的方法. <!DOCTYPE html><html lang="en&quo ...
- Scrum Meeting NO.9
Scrum Meeting No.9 1.会议内容 2.任务清单 徐越 序号 近期的任务 进行中 已完成 1 代码重构:前端通讯模块改为HttpClient+Json √ 2 "我" ...