题目传送门

题目大意

给出一个\(n\)个点的树,每个点有权值。有\(m\)次操作,每次要么查询一条链上的最大子段和,要么把一条链的权值都修改为一个常数。

\(n,m\le 10^5\)

思路

如果是一维的话,我们不难列出动态\(\texttt{dp}\)转移式:

\[\begin{bmatrix}0&a_i&0\\-\infty&a_i&0\\-\infty&-\infty&0\end{bmatrix}\begin{bmatrix}g_{i-1}\\f_{i-1}\\0\end{bmatrix}=\begin{bmatrix}g_i\\f_i\\0\end{bmatrix}
\]

不懂得话可以去看一下GSS1的题解。

这道题要求一个链的答案,那我们直接求出这个链的矩阵之积即可,用树剖就好了,修改也很简单。需要注意的是,矩阵乘法有没有交换律的,所以需要维护两种不同方向的矩阵之积。

这道题有点卡常,所以快速幂不能朴素快速幂,而是找一下规律,具体见代码。

\(\texttt{Code}\)

#include <bits/stdc++.h>
using namespace std; #define Int register int
#define INF 0x7f7f7f7f
#define MAXN 100005 template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');} int n,m,wei[MAXN]; struct Matrix{
int val[3][3];
Matrix(){memset (val,0xcf,sizeof (val));}
int* operator [](int x){return val[x];}
Matrix operator * (const Matrix &p)const{
Matrix New;
for (Int i = 0;i < 3;++ i)
for (Int j = 0;j < 3;++ j)
for (Int k = 0;k < 3;++ k)
New[i][j] = max (New[i][j],val[i][k] + p.val[k][j]);
return New;
}
}; Matrix init (int v){
Matrix A;
A[0][0] = A[0][2] = A[1][2] = A[2][2] = 0,A[0][1] = A[1][1] = v;
return A;
} Matrix III (){
Matrix res;
for (Int i = 0;i < 3;++ i) res[i][i] = 0;
return res;
} Matrix qkpow (int v,int k){
Matrix A;
A[0][0] = A[2][2] = 0;
A[0][1] = max (v,k * v),A[0][2] = A[1][2] = max (0,k * v);
A[1][1] = k * v;
return A;
} struct edge{
int v,nxt;
}e[MAXN << 1]; int toop = 1,head[MAXN]; void Add_Edge (int u,int v){
e[++ toop] = edge {v,head[u]},head[u] = toop;
e[++ toop] = edge {u,head[v]},head[v] = toop;
} int Index,dep[MAXN],siz[MAXN],son[MAXN],dfn[MAXN],par[MAXN],top[MAXN],tur[MAXN]; void dfs1 (int u,int fa){
par[u] = fa,dep[u] = dep[fa] + 1,siz[u] = 1;
for (Int i = head[u];i;i = e[i].nxt){
int v = e[i].v;
if (v == fa) continue;
dfs1 (v,u),siz[u] += siz[v];
if (siz[v] > siz[son[u]]) son[u] = v;
}
} void dfs2 (int u,int Top){
dfn[u] = ++ Index,tur[Index] = u,top[u] = Top;
if (son[u]) dfs2 (son[u],Top);
for (Int i = head[u];i;i = e[i].nxt){
int v = e[i].v;
if (v == par[u] || v == son[u]) continue;
dfs2 (v,v);
}
} struct Segment{
#define len(x) (tree[x].r-tree[x].l+1)
struct node{
int l,r,tag;Matrix Sum[2];
}tree[MAXN << 2];
void Pushup (int x){
tree[x].Sum[0] = tree[x << 1].Sum[0] * tree[x << 1 | 1].Sum[0];
tree[x].Sum[1] = tree[x << 1 | 1].Sum[1] * tree[x << 1].Sum[1];
}
void Pushadd (int x,int v){
tree[x].tag = v;
tree[x].Sum[0] = tree[x].Sum[1] = qkpow (v,len (x));
}
void Pushdown (int x){
if (tree[x].tag == INF) return ;
Pushadd (x << 1,tree[x].tag),Pushadd (x << 1 | 1,tree[x].tag);
tree[x].tag = INF;
}
void build (int i,int l,int r){
tree[i].l = l,tree[i].r = r,tree[i].tag = INF;
if (l == r) return tree[i].Sum[0] = tree[i].Sum[1] = init(wei[tur[l]]),void ();
int mid = (l + r) >> 1;
build (i << 1,l,mid),build (i << 1 | 1,mid + 1,r);
Pushup (i);
}
Matrix query (int i,int l,int r,int type){
if (tree[i].l >= l && tree[i].r <= r) return tree[i].Sum[type];
int mid = (tree[i].l + tree[i].r) >> 1;
Pushdown (i);
if (r <= mid) return query (i << 1,l,r,type);
else if (l > mid) return query (i << 1 | 1,l,r,type);
else return !type ? query (i << 1,l,r,type) * query (i << 1 | 1,l,r,type) : query (i << 1 | 1,l,r,type) * query (i << 1,l,r,type);
}
void Change (int i,int l,int r,int v){
if (tree[i].l >= l && tree[i].r <= r) return Pushadd (i,v);
int mid = (tree[i].l + tree[i].r) >> 1;
Pushdown (i);
if (l <= mid) Change (i << 1,l,r,v);
if (r > mid) Change (i << 1 | 1,l,r,v);
Pushup (i);
}
#undef len(x)
}Tree; int QueryChain (int x,int y){
Matrix A,B;A = B = III();
while (top[x] ^ top[y]){
if (dep[top[x]] > dep[top[y]]){
A = A * Tree.query (1,dfn[top[x]],dfn[x],1);
x = par[top[x]];
}
else{
B = Tree.query (1,dfn[top[y]],dfn[y],0) * B;
y = par[top[y]];
}
}
if (dfn[x] < dfn[y]) B = Tree.query (1,dfn[x],dfn[y],0) * B;
else A = A * Tree.query (1,dfn[y],dfn[x],1);A = A * B;
return max (A[0][1],A[0][2]);
} void UpdateChain (int x,int y,int v){
while (top[x] ^ top[y]){
if (dep[top[x]] < dep[top[y]]) swap (x,y);
Tree.Change (1,dfn[top[x]],dfn[x],v);
x = par[top[x]];
}
if (dfn[x] > dfn[y]) swap (x,y);
Tree.Change (1,dfn[x],dfn[y],v);
} signed main(){
read (n);
for (Int i = 1;i <= n;++ i) read (wei[i]);
for (Int i = 2,u,v;i <= n;++ i) read (u,v),Add_Edge (u,v);
dfs1 (1,0),dfs2 (1,1),Tree.build (1,1,n);
read (m);
while (m --){
int opt,a,b,c;
read (opt,a,b);
if (opt == 1) write (QueryChain (a,b)),putchar ('\n');
else read (c),UpdateChain (a,b,c);
}
return 0;
}

题解 SP6779 【GSS7 - Can you answer these queries VII】的更多相关文章

  1. SP6779 GSS7 - Can you answer these queries VII

    纯数据结构题,没有思维难度.直接用线段树求最大子段和的方法完成树上路径的合并.注意链上合并顺序要符合序列的前后顺序. #include <cstdio> #include <cstr ...

  2. SP6779 GSS7 - Can you answer these queries VII(线段树,树链剖分)

    水题,只是坑点多,\(tag\)为\(0\)时可能也要\(pushdown\),所以要\(bool\)标记是否需要.最后树链剖分询问时注意线段有向!!! #include <cstring> ...

  3. SPOJ GSS7 - Can you answer these queries VII

    板的不能再板,链剖+线段树或者是LCT随便维护. 感觉唯一要注意的是跳链的时候要对$x$向上跳和$y$向上跳的情况分开讨论,而不能直接$swap$,因为只有两段接触的端点才能相互合并,而且每一次向上跳 ...

  4. SPOJ GSS7 Can you answer these queries VII ——树链剖分 线段树

    [题目分析] 问题放到了树上,直接链剖+线段树搞一搞. 调了300行+. (还是码力不够) [代码] #include <cstdio> #include <cstring> ...

  5. [题解] SPOJ GSS1 - Can you answer these queries I

    [题解] SPOJ GSS1 - Can you answer these queries I · 题目大意 要求维护一段长度为 \(n\) 的静态序列的区间最大子段和. 有 \(m\) 次询问,每次 ...

  6. GSS7 spoj 6779. Can you answer these queries VII 树链剖分+线段树

    GSS7Can you answer these queries VII 给出一棵树,树的节点有权值,有两种操作: 1.询问节点x,y的路径上最大子段和,可以为空 2.把节点x,y的路径上所有节点的权 ...

  7. 6779. Can you answer these queries VII - SPOJ

    Given a tree with N ( N<=100000 ) nodes. Each node has a interger value x_i ( |x_i|<=10000 ). ...

  8. GSS4 2713. Can you answer these queries IV 线段树

    GSS7 Can you answer these queries IV 题目:给出一个数列,原数列和值不超过1e18,有两种操作: 0 x y:修改区间[x,y]所有数开方后向下调整至最近的整数 1 ...

  9. BZOJ2482: [Spoj1557] Can you answer these queries II

    题解: 从没见过这么XXX的线段树啊... T_T 我们考虑离线做,按1-n一个一个插入,并且维护区间[ j,i](i为当前插入的数)j<i的最优值. 但这个最优值!!! 我们要保存历史的最优值 ...

随机推荐

  1. BST B树 B+树

    二叉排序树/二叉搜索树 (BST) 定义 左子树节点值<根节点值<右子树节点值 默认不允许两个节点的关键值相同 进行中序遍历可以得到递增的有序序列 查找效率 取决与树的高度,最好O(log ...

  2. Hutool中那些常用的工具类和方法

    Hutool中那些常用的工具类和方法 Hutool是一个Java工具包,它帮助我们简化每一行代码,避免重复造轮子.如果你有需要用到某些工具方法的时候,不妨在Hutool里面找找,可能就有.本文将对Hu ...

  3. SpringBoot - 搭建静态资源存储服务器

    目录 前言 环境 实现效果 具体实现 文件上传 配置类 上传接口 上传实现 辅助类 实体 上传测试 文件访问 配置类 项目源码 前言 记录下SpringBoot下静态资源存储服务器的搭建. 环境 wi ...

  4. Docker小白到实战之Dockerfile解析及实战演示,果然顺手

    前言 使用第三方镜像肯定不是学习Docker的最终目的,最想要的还是自己构建镜像:将自己的程序.文件.环境等构建成自己想要的应用镜像,方便后续部署.启动和维护:而Dockerfile就是专门做这个事的 ...

  5. Tars | 第4篇 Subset路由规则业务分析与源码探索

    目录 前言 1. Subset不是负载均衡 1.1 任务需求 1.2 负载均衡源码结构图 1.3 负载均衡四种调用器 1.4 新增两种负载均衡调用器 1.5 Subset应该是"过滤&quo ...

  6. Tricks

    由于本人着实有些菜,因此在此积累一些巧妙的 \(Tricks\) ,以备不时之需... 与其说是 \(Tricks\) 不如说是学习笔记?? 数学 组合数 常见的数列 斐波那契数列 图论 树论 \(P ...

  7. asp.net 工具

    http://www.jb51.net/article/92465.htm 这篇文章列出了针对ASP.NET开发人员的有用工具. 工具 1.Visual Studio Visual Studio Pr ...

  8. k8s核心资源之namespace与pod污点容忍度生命周期进阶篇(四)

    目录 1.命名空间namespace 1.1 什么是命名空间? 1.2 namespace应用场景 1.3 namespacs常用指令 1.4 namespace资源限额 2.标签 2.1 什么是标签 ...

  9. go相关

    mac 上build go  如果想要在centos上面执行 必须使用下面的方式 CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o hello ...

  10. PHP的命令行扩展Readline相关函数学习

    PHP 作为一个 Web 开发语言,相对来说,命令行程序并不是它的主战场.所以很多年轻的 PHP 开发者可能连命令行脚本都没有写过,更别提交互式的命令操作了.而今天,我们带来的这个扩展就是针对 PHP ...