题目传送门

题目大意

给出一个\(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. Workflow Core + asp.net core 5.0 实现简单审批工作流

    我们知道企业业务系统到处都可以审批工作流的,但也很少有像OA系统一样复杂多级多条件的审批工作流需要设计,所以我们需要一个轻量级的容易上手的workflow框架,通过GitHub,我发现danielge ...

  2. servlet+Ajax开发web工程

    前言 因为目前基本已经不会再用到servlet+jsp开发项目了,基本都是使用框架来开发:我们常用的框架都是基于servlet来封装的,该阶段只需要了解一下tomcat如何使用,servlet的生命周 ...

  3. Spring Boot +Vue 项目实战笔记(二):前后端结合测试(登录页面开发)

    前言:关于开发环境 每位 Coder 都有自己偏好的开发工具,从大的方面划分主要有文本编辑器流和 IDE 流两种,我有一段时间也喜欢用编辑器(Sublime Text.Vim),但对我来说开发效率确实 ...

  4. 字符型:char

    字符型:char 字符变量的定义和输出 字符变量用于存储一个单一字符,在C语言中用char表示,其中每个字符变量都会占用1个字节.在给字符型变量赋值时,需要用一对因为半角格式的单引号('   ')把字 ...

  5. 存储系统管理(三)——磁盘配额及lvm逻辑卷管理

    Linux是一个多用户的操作系统,系统有很多用户,就必须限制每个用户的保存空间,配额就是来管理用户空间的,配额只是针对与设备而言. 1.新建一个分区 2.格式化分区为xfs文件系统 3.将其以配额的方 ...

  6. 第20篇-加载与存储指令之ldc与_fast_aldc指令(2)

    ldc指令将int.float.或者一个类.方法类型或方法句柄的符号引用.还可能是String型常量值从常量池中推送至栈顶. 这一篇介绍一个虚拟机规范中定义的一个字节码指令ldc,另外还有一个虚拟机内 ...

  7. Identity用户管理入门五(登录、注销)

    一.建立LoginViewModel视图模型 using System.ComponentModel.DataAnnotations; namespace Shop.ViewModel { publi ...

  8. MongoDB索引的简单理解

    目录 MongoDB索引 1.语法准备 2.数据准备: 3.索引 3.1 唯一索引 3.2 单键索引 3.3 多键索引 3.4 复合索引 3.5 交叉索引 3.6 部分索引 3.7覆盖索引 3.8 全 ...

  9. 有个计算机专业的学妹问我:我这个zip文件密码破解运行起来为什么内存爆了?

    1.这篇博文的由来 2.跑下错误代码,找病根 先把学妹发给我的错误代码放上,能发现他为了提高速度加了多线程的代码,很聪明哦: import zipfile import itertools from ...

  10. java的运行时数据区域

    最近在看<深入理解Java虚拟机>,书中给了几个例子,比较好的说明了几种OOM(OutOfMemory)产生的过程,大部分的程序员在写程序时不会太关注Java运行时数据区域的结构: 1.程 ...