题目链接

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

好的说题解。

可以发现(我并没发现),题中的两种操作都可以用矩阵来表示。假如用下面这个矩阵表示一个节点的状态

\[\begin{bmatrix}1 & v & t\end{bmatrix}
\]

那么操作1可以表示为

\[\begin{bmatrix}1 & v & t\end{bmatrix} *\begin{bmatrix}1 & d & 0\\0 & 1 & 0\\0 & 0 & 1\end{bmatrix}=\begin{bmatrix}1 & v + d & t\end{bmatrix}
\]

(可以看出那个1就是用来给v加上d的)

然后操作2可以表示为

\[\begin{bmatrix}1 & v & t\end{bmatrix} *\begin{bmatrix}1 & 0 & 0\\0 & 1 & d\\0 & 0 & 1\end{bmatrix}=\begin{bmatrix}1 & v & t + v * d\end{bmatrix}
\]

这样两种操作就都可以通过矩阵来做啦。

然后根据题目要求显然需要树链剖分,线段树上维护矩阵就可以了。矩阵乘法满足结合律,所以可以正常地下放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 树据结构 | 树链剖分 矩阵乘法的更多相关文章

  1. luoguP4719 【模板】动态 DP 线段树+树链剖分+矩阵乘法+动态DP

    题目描述 给定一棵n个点的树,点带点权. 有m次操作,每次操作给定x,y,表示修改点x的权值为y. 你需要在每次操作之后求出这棵树的最大权独立集的权值大小. 输入输出格式 输入格式: 第一行,n,m分 ...

  2. 树的统计Count---树链剖分

    NEFU专项训练十和十一——树链剖分 Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t ...

  3. [LOJ3014][JOI 2019 Final]独特的城市——树的直径+长链剖分

    题目链接: [JOI 2019 Final]独特的城市 对于每个点,它的答案最大就是与它距离最远的点的距离. 而如果与它距离为$x$的点有大于等于两个,那么与它距离小于等于$x$的点都不会被计入答案. ...

  4. BZOJ 1036: [ZJOI2008]树的统计Count-树链剖分(点权)(单点更新、路径节点最值、路径求和)模板,超级认真写了注释啊啊啊

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 23015  Solved: 9336[Submit ...

  5. 6.3 省选模拟赛 Decompose 动态dp 树链剖分 set

    LINK:Decompose 看起来很难 实际上也很难 考验选手的dp 树链剖分 矩阵乘法的能力. 容易列出dp方程 暴力dp 期望得分28. 对于链的情况 容易发现dp方程可以转矩阵乘法 然后利用线 ...

  6. LOJ3053 十二省联考2019 希望 容斥、树形DP、长链剖分

    传送门 官方题解其实讲的挺清楚了,就是锅有点多-- 一些有启发性的部分分 L=N 一个经典(反正我是不会)的容斥:最后的答案=对于每个点能够以它作为集合点的方案数-对于每条边能够以其两个端点作为集合点 ...

  7. 219.01.19 bzoj3252: 攻略(长链剖分+贪心)

    传送门 长链剖分好题. 题意:给一棵带点权的树,可以从根节点到任一叶节点走kkk次,走过的点只能计算一次,问kkk次走过的点点权值和最大值. 思路: 考虑将整棵树带权长链剖分,这样链与链之间是不会重复 ...

  8. CF718C Sasha and Array 线段树 + 矩阵乘法

    有两个操作: 将 $[l,r]$所有数 + $x$ 求 $\sum_{i=l}^{r}fib(i)$ $n=m=10^5$   直接求不好求,改成矩阵乘法的形式:  $a_{i}=M^x\times ...

  9. 51Nod 1199 Money out of Thin Air (树链剖分+线段树)

    1199 Money out of Thin Air  题目来源: Ural 基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题  收藏  关注 一棵有N个节点的树,每 ...

随机推荐

  1. C# LINQ 详解 From Where Select Group Into OrderBy Let Join

    目录 1. 概述 2. from子句 3. where子句 4. select子句 5. group子句 6. into子句 7. 排序子句 8. let子句 9. join子句 10. 小结 1. ...

  2. 浅谈左偏树在OI中的应用

    Preface 可并堆,一个听起来很NB的数据结构,实际上比一般的堆就多了一个合并的操作. 考虑一般的堆合并时,当我们合并时只能暴力把一个堆里的元素一个一个插入另一个堆里,这样复杂度将达到\(\log ...

  3. R绘图 第十二篇:散点图(高级)

    散点图用于描述两个连续性变量间的关系,三个变量之间的关系可以通过3D图形或气泡来展示,多个变量之间的两两关系可以通过散点图矩阵来展示. 一,添加了最佳拟合曲线的散点图 使用基础函数plot(x,y)来 ...

  4. Docker网络解决方案 - Calico部署记录

    简单来说,实现docker跨主机容器间通信,常用的第三方网络方案是Flannel,Weave,Calico:Flannel会为每个host分配一个subnet,容器从这个subnet中分配ip,这些i ...

  5. 网站每日PV/IP统计/总带宽/URL统计脚本分享(依据网站访问日志)

    在平时的运维工作中,我们运维人员需要清楚自己网站每天的总访问量.总带宽.ip统计和url统计等.虽然网站已经在服务商那里做了CDN加速,所以网站流量压力都在前方CDN层了像每日PV,带宽,ip统计等数 ...

  6. Linux下的Mongodb部署应用梳理

    一.Mongodb简介  官网地址:http://www.mongodb.org/ MongoDB是一个高性能,开源,无模式的文档型数据库,是当前NoSql数据库中比较热门的一种.MongoDB 是一 ...

  7. Python常见字符编码间的转换

    主要内容:     1.Unicode 和 UTF-8的爱恨纠葛     2.字符在硬盘上的存储     3.编码的转换     4.验证编码是否转换正确     5.Python bytes类型 前 ...

  8. omnigraffle 的一些总结

    http://jingyan.baidu.com/article/fcb5aff7a16337edab4a714d.html Omnigraffle绘制连接线时从任意点开始 点击直线工具后,在右侧设置 ...

  9. B. Forgery

    链接 [http://codeforces.com/contest/1059/problem/B] 题意 要伪造医生签名,先给你医生的签名nm的网格'.'表示空白',#'表示墨水,你的笔可以这么画以一 ...

  10. 删除运行时权限不足,cmd开启管理员

    管理员帐号活跃代码:net user administrator /active:yes 搜索cmd-右键以管理员身份运行 切换administrator帐号登录 操作后最后关闭这么高的权限,避免被非 ...