题传

upd on 2023.10.03 补充了代码以及一些实现细节。

自己写的关于这类剖分方法的 \(blog\)

题意简述

称一条链和与其有连边的点 构成的点集 为 “毛毛虫”,链上的点为 “毛点”,某个 “毛点” \(x\) 的脚(与之右边但非链点)的点集为 \(T_x\)。

操作:给出一条 “毛毛虫” 的两端 \(u, v\),将该“毛毛虫”内的点的点权改为 \(k\);

询问:开始有个空序列 \(q\),给出一条 “毛毛虫” 的头、尾 \(u, v\),从头到尾按链上的顺序遍历 “毛点”,每走到一个 “毛点” \(x\),现将 \(x\) 的点权加入 \(q\),然后将 \(T_x\) 内的点的点权按点编号从小到大顺序放入 \(q\)。操作完后,询问 \(q\) 的最大子段和(可以为 0);

主要思路

考虑根据重链构造满足题目修改要求的 dfs 序,然后直接线段树维护。

设 \(S_{Z}\) 为将重链 \(Z\) 作为 “毛毛虫” 时的 \(q\),特别的,此时 \(q\) 不包含 \(Z\) 链顶的父亲。

我们构造的 dfs 序满足:

每条重链 \(Z\) 的 \(S_{Z}\) 的编号连续;

因为跳重链时会出现半条链的情况,所以需要额外满足:

对于每条重链上的点 \(x\),\(x\) 与 \(x\) 所有在 \(S_{Z}\) 中的拓展点(即 \(T_x\))编号连续;

注意到题目中询问分向上走和向下走两种,因此还有一个限制:

重链点的编号的偏序关系和重链点的深度偏序关系全部相同或相反(分别对应向下和向下的情况)。

对于第三个限制,由于不能同时正序和倒序,所以构造两个不同的 dfn 序分别满足相同和相反,分开两棵线段树进行维护。

考虑构造,由于我们树剖时都是向上跳,因此我们对于某条重链,自底向上遍历每个点 \(x\),对于向上跳的 dfs 序,先给 \(x\) 编号后再给 \(T_{x}\) 编号;对于向下跳的 dfs 序,先给 \(T_{x}\) 编号再给 \(x\) 编号。这样子,对于我们询问中向下跳的部分,只要整体翻转就可以得到正序遍历之后的信息。

void cover(int x){
if(son[x]) cover(son[x]);
dfT[x][0]=dfn[0]+1, nfT[x][0]=nfd[0]+1;if(!dfn[x]) dfn[x]=++dfn[0];
for(auto v:G[x]) if(son[x]^v) dfn[v]=++dfn[0];reverse(G[x].begin(), G[x].end());
for(auto v:G[x]) if(son[x]^v) nfd[v]=++nfd[0];reverse(G[x].begin(), G[x].end());
if(!nfd[x]) nfd[x]=++nfd[0];dfT[x][1]=dfn[0], nfT[x][1]=nfd[0];
}
void dfs(int x, int rt){
top[x]=rt;if(x==rt) cover(x);if(son[x]) dfs(son[x], rt);
int c=0;for(auto v:G[x]) if(son[x]^v) dfs(v, v), ++c;else pos[x]=c;
}

其中 \(dfn_{x}, nfd_{x}\) 分别为 \(x\) 上跳/下跳的 dfs 序。\(dfT_{x, 0, 1}, nfT_{x, 0/1}\) 则维护了 \(T_{x}\) 的 dfs 序区间。

注意到这样的构造有点不完美:一条重链 \(Z\) 的链顶的 dfs 序与 \(Z\) 可能是分离的。但这只需要 \(O(1)\) 的分讨量,因此是利大于弊的。然而代码细节变得巨多。

代码细节

十分建议先写性质 D,因为询问边界很多需要特殊处理,而修改是覆盖,边界问题基本不用考虑。

对于修改:直接模拟树剖 LCA,注意跳重链时,重链顶部单独拿出来修改,当前所在的半条重链底部的重儿子也要考虑。最后注意 LCA 的父亲也要修改。

对于询问:仍是模拟 LCA,链底/顶单独拿出来,跳完一条重链的时候,可以先将重链顶部的值变为 0,这样就不需要再在上面考虑排除掉这些多余的信息。最后再修改回来即可。

更多的细节可以看代码注释。

Code

#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <cctype>
#include <vector>
#include <queue>
#include <bitset>
#define vi vector<int>
#define pb push_back
#define mp make_pair
#define st first
#define nd second
using namespace std;
typedef long long ll;
typedef pair <int, int> Pii;
const int INF=1e9+1;
const int cp=998244353;
inline int mod(int x){if(x>=cp) x-=cp;if(x<0) x+=cp;return x;}
inline void plust(int &x, int y){x=mod(x+y);return ;}
inline void minut(int &x, int y){x=mod(x-y);return ;}
inline int read(){
char ch=getchar();int x=0, f=1;
while(!isdigit(ch)){if(ch=='-') f=-1; ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
inline void write(int x){
if(x<0) putchar('-'), x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
inline int ksm(int a, int b=cp-2){
int ret=1;
for(; b; b>>=1, a=1ll*a*a%cp)
if(b&1) ret=1ll*ret*a%cp;
return ret;
}
const int N=1e5+5;
inline ll mx(ll x, ll y){return x>y?x:y;}
struct node{ll sum, lp, rp, ans;};
inline node Node(ll v){return (node){v, mx(v, 0), mx(v, 0), mx(v, 0)};}
const node O=(node){0, 0, 0, 0};
node operator + (node L, node R){
node res=O;res.sum=L.sum+R.sum;
res.lp=mx(L.lp, L.sum+R.lp);res.rp=mx(R.rp, R.sum+L.rp);
res.ans=mx(mx(L.ans, R.ans), L.rp+R.lp);return res;
}
struct Tree{
#define ls k<<1
#define rs k<<1|1
#define mid ((l+r)>>1)
node tr[N<<2];int tag[N<<2], len[N<<2];
inline void upd(int k, int v){ll p=1ll*len[k]*v;tr[k]=Node(p);tag[k]=v;}
inline void pushdown(int k){if(tag[k]!=INF) upd(ls, tag[k]), upd(rs, tag[k]);tag[k]=INF;}
inline void pushup(int k){tr[k]=tr[ls]+tr[rs];}
void build(int k, int l, int r){
len[k]=r-l+1;tag[k]=INF;tr[k]=O;if(l==r) return ;
build(ls, l, mid);build(rs, mid+1, r);
}
int m, x, y, v;inline void init(int n){m=n;build(1, 1, m);}
int update(int k, int l, int r){
if(l==r){int r=tr[k].sum;tr[k]=Node(v);return r;}pushdown(k);
int res=(x<=mid)?update(ls, l, mid):update(rs, mid+1, r);pushup(k);return res;
}
inline int flp(int xx, int vv=0){if(!xx) return 0;x=xx, v=vv;int res=update(1, 1, m);return res;}
void modify(int k, int l, int r){
if(x>r||y<l||x>y) return ;if(x<=l&&r<=y) return upd(k, v);pushdown(k);
modify(ls, l, mid);modify(rs, mid+1, r);pushup(k);
}
inline void mdy(int xx, int yy, int vv){x=xx, y=yy, v=vv;modify(1, 1, m);}
node query(int k, int l, int r){
if(x>r||y<l||x>y) return O;if(x<=l&&r<=y) return tr[k];pushdown(k);
return query(ls, l, mid)+query(rs, mid+1, r);
}
inline node qry(int xx, int yy){if(!xx||!yy) return O;x=xx, y=yy;node res=query(1, 1, m);return res;}
#undef ls
#undef rs
#undef mid
}U, D;
int L[N], n, T, dfn[N], dfT[N][2], nfd[N], nfT[N][2], top[N], dep[N], son[N], pos[N], siz[N], fa[N];vi G[N];
void cover(int x){
if(son[x]) cover(son[x]);
dfT[x][0]=dfn[0]+1, nfT[x][0]=nfd[0]+1;if(!dfn[x]) dfn[x]=++dfn[0];
for(auto v:G[x]) if(son[x]^v) dfn[v]=++dfn[0];reverse(G[x].begin(), G[x].end());
for(auto v:G[x]) if(son[x]^v) nfd[v]=++nfd[0];reverse(G[x].begin(), G[x].end());
if(!nfd[x]) nfd[x]=++nfd[0];dfT[x][1]=dfn[0], nfT[x][1]=nfd[0];
}
void dfs(int x, int rt){
top[x]=rt;if(x==rt) cover(x);if(son[x]) dfs(son[x], rt);
int c=0;for(auto v:G[x]) if(son[x]^v) dfs(v, v), ++c;else pos[x]=c;
}
inline void chg(int x, int y, int v){
while(top[x]^top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x, y);
U.mdy(dfT[x][0], dfT[top[x]][1], v);U.flp(dfn[top[x]], v), U.flp(dfn[son[x]], v);
D.mdy(nfT[x][0], nfT[top[x]][1], v);D.flp(nfd[top[x]], v), D.flp(nfd[son[x]], v);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x, y);
U.mdy(dfT[y][0], dfT[x][1], v);U.flp(dfn[x], v);U.flp(dfn[fa[x]], v);U.flp(dfn[son[y]], v);
D.mdy(nfT[y][0], nfT[x][1], v);D.flp(nfd[x], v);D.flp(nfd[fa[x]], v);D.flp(nfd[son[y]], v);
}
inline int LCA(int x, int y){
while(top[x]^top[y]){if(dep[top[x]]<dep[top[y]]) swap(x, y);x=fa[top[x]];}
return dep[x]<dep[y]?x:y;
}
struct tmper{int x, v, op;};
inline node calc(int x, bool flg){
//计算 x 的所有儿子(包括重儿子)
if(!son[x]) return O;node res=O;
if(flg){
int l=nfT[x][0], r=nfT[x][1]-(x!=top[x]), mid=r-pos[x]+1;
// printf("calc [%d %d]+%d+[%d %d]\n", l, mid-1, nfd[son[x]], mid, r);
res=D.qry(l, mid-1)+D.qry(nfd[son[x]], nfd[son[x]])+D.qry(mid, r);
// printf("ask %d %lld[%lld %lld]\n", x, res.sum, res.lp, res.rp);
}
else{
int l=dfT[x][0]+(x!=top[x]), r=dfT[x][1], mid=l+pos[x]-1;
res=U.qry(l, mid)+U.qry(dfn[son[x]], dfn[son[x]])+U.qry(mid+1, r);
}
return res;
}
ll ask(int x, int y){
node res=O, ser=O;int lca=LCA(x, y);vector <tmper> Q;
while(top[x]^top[lca]){
res=res+U.qry(dfn[x], dfn[x])+calc(x, 0);
if(x^top[x]) res=res+U.qry(dfT[fa[x]][0], dfT[son[top[x]]][1])+
U.qry(dfn[top[x]], dfn[top[x]])+U.qry(dfT[top[x]][0], dfT[top[x]][1]);//注意链顶单独分开
Q.pb((tmper){dfn[top[x]], U.flp(dfn[top[x]]), 0});/*将链顶标记 删除*/x=fa[top[x]];
}
if(x!=lca){
res=res+U.qry(dfn[x], dfn[x])+calc(x, 0)+U.qry(dfT[fa[x]][0], dfT[son[lca]][1]);
Q.pb((tmper){dfn[son[lca]], U.flp(dfn[son[lca]]), 0});
}
while(top[y]^top[lca]){
ser=ser+calc(y, 1)+D.qry(nfd[y], nfd[y]);//注意合并顺序
if(y^top[y]) ser=ser+D.qry(nfT[fa[y]][0], nfT[son[top[y]]][1])+
D.qry(nfT[top[y]][0], nfT[top[y]][1])+D.qry(nfd[top[y]], nfd[top[y]]);
Q.pb((tmper){nfd[top[y]], D.flp(nfd[top[y]]), 1});y=top[y];
if(fa[y]==lca) Q.pb((tmper){dfn[y], U.flp(dfn[y]), 0});y=fa[y];
}
if(y!=lca){
ser=ser+calc(y, 1)+D.qry(nfd[y], nfd[y])+D.qry(nfT[fa[y]][0], nfT[son[lca]][1]);
Q.pb((tmper){dfn[son[lca]], U.flp(dfn[son[lca]]), 0});//注意理解这里
}
res=res+U.qry(dfn[lca], dfn[lca])+U.qry(dfn[fa[lca]], dfn[fa[lca]])+calc(lca, 0);
for(auto [x, v, op]:Q) (op?D:U).flp(x, v);swap(ser.lp, ser.rp);
return (res+ser).ans;
}
signed main(){
T=read(), n=read();
for(int i=1; i<=n; ++i) L[i]=read(), siz[i]=1;dep[1]=1;
for(int i=2; i<=n; ++i) G[fa[i]=read()].pb(i), dep[i]=dep[fa[i]]+1;
for(int i=n; i>=2; --i) siz[fa[i]]+=siz[i], son[fa[i]]=(siz[son[fa[i]]]<siz[i])?i:son[fa[i]];
dfn[1]=++dfn[0], nfd[1]=++nfd[0], dfs(1, 1);U.init(n);D.init(n);dfn[0]=nfd[0]=0;
for(int i=1; i<=n; ++i) U.flp(dfn[i], L[i]), D.flp(nfd[i], L[i]);
int q=read();
// for(int i=1; i<=n; ++i)
// printf("%d:%d[%d %d] %d[%d %d] %d %d\n", i, dfn[i], dfT[i][0], dfT[i][1],
// nfd[i], nfT[i][0], nfT[i][1], top[i], pos[i]);
for(int i=1; i<=q; ++i){
int op=read(), u=read(), v=read();
if(!op) chg(u, v, read());
else printf("%lld\n", ask(u, v));
}
return 0;
}

Luogu P8479 「GLR-R3」谷雨的更多相关文章

  1. [Luogu 3701] 「伪模板」主席树

    [Luogu 3701] 「伪模板」主席树 这是一道网络流,不是主席树,不是什么数据结构,而是网络流. 题目背景及描述都非常的暴力,以至于 Capella 在做此题的过程中不禁感到生命流逝. S 向 ...

  2. [Luogu] P3701 「伪模板」主席树

    题目背景 byx和手气君都非常都非常喜欢种树.有一天,他们得到了两颗奇怪的树种,于是各自取了一颗回家种树,并约定几年后比一比谁种出来的树更加牛x. 题目描述 很快,这棵树就开花结果了.byx和手气君惊 ...

  3. 「算法笔记」树形 DP

    一.树形 DP 基础 又是一篇鸽了好久的文章--以下面这道题为例,介绍一下树形 DP 的一般过程. POJ 2342 Anniversary party 题目大意:有一家公司要举行一个聚会,一共有 \ ...

  4. loj#2020 「AHOI / HNOI2017」礼物 ntt

    loj#2020 「AHOI / HNOI2017」礼物 链接 bzoj没\(letex\),差评 loj luogu 思路 最小化\(\sum\limits_1^n(a_i-b_i)^2\) 设改变 ...

  5. [LOJ 2022]「AHOI / HNOI2017」队长快跑

    [LOJ 2022]「AHOI / HNOI2017」队长快跑 链接 链接 题解 不难看出,除了影响到起点和终点的射线以外,射线的角度没有意义,因为如果一定要从该射线的射出一侧过去,必然会撞到射线 因 ...

  6. 「GXOI / GZOI2019」宝牌一大堆 (DP)

    题意 LOJ传送门 题解 可以发现「七对子」 和 「国士无双」直接暴力就行了. 唯一的就是剩下的"3*4+2". 考试的时候写了个爆搜剪枝,开了O2有50pts.写的时候发现可以D ...

  7. 「NOIP 2020」微信步数(计数)

    「NOIP 2020」微信步数(Luogu P7116) 题意: 有一个 \(k\) 维场地,第 \(i\) 维宽为 \(w_i\),即第 \(i\) 维的合法坐标为 \(1, 2, \cdots, ...

  8. 「算法笔记」快速数论变换(NTT)

    一.简介 前置知识:多项式乘法与 FFT. FFT 涉及大量 double 类型数据操作和 \(\sin,\cos\) 运算,会产生误差.快速数论变换(Number Theoretic Transfo ...

  9. 前端构建工具之gulp(一)「图片压缩」

    前端构建工具之gulp(一)「图片压缩」 已经很久没有写过博客了,现下终于事情少了,开始写博吧 今天网站要做一些优化:图片压缩,资源合并等 以前一直使用百度的FIS工具,但是FIS还没有提供图片压缩的 ...

  10. fir.im Weekly - 如何打造 Github 「爆款」开源项目

    最近 Android 转用 Swift 的传闻甚嚣尘上,Swift 的 Github 主页上已经有了一次 merge>>「Port to Android」,让我们对 Swift 的想象又多 ...

随机推荐

  1. SQL Server 5105 和 1802 错误的触发方式和解决方式之一

    一般导致这两个错误的原因是:文件路径错误 还有的说,可能是文件权限问题,详情见权限错误纠正方式 错误代码 create database teaching on primary ( name = te ...

  2. JBoltAI Function Call技术解析:如何实现AI模型与企业系统的无缝对话

    JBoltAI Function Call技术解析: 如何实现AI模型与企业系统的无缝对话 在企业级AI应用开发中,如何让大模型能力与现有系统高效协同一直是技术难点.JBoltAI框架通过Functi ...

  3. 使用SymPy求解矩阵微分方程

    引言 在数学.物理.工程等领域,微分方程常常被用来描述系统的变化和动态过程.对于多变量系统或者多方程系统,矩阵微分方程是非常常见的,它可以用来描述如电路.控制系统.振动系统等复杂的动态行为.今天,我们 ...

  4. MySQL隐藏手机号

    1.实现方法 通过MySQL的left.right.contact函数实现 2.语法说明 contact()函数 作用:将多个字符串连接成一个字符串 语法:concat(字符串1,....,字符串n) ...

  5. TreeSet的add方法源码分析

    一.JDK 1.8 中 TreeSet 的 add 方法源码详细分析 TreeSet 是 Java 集合框架中的一个有序集合类,基于红黑树(TreeMap)实现.TreeSet 的 add 方法用于向 ...

  6. ShardingJdbc学习笔记

    Mysql主从复制遇到问题 安装mysql Install/Remove of the Service Denied!错误的解决办法

  7. 配置Thymeleaf模板引擎

    1).thymeleaf-starter: 关闭缓存 2).静态资源都放在static文件夹下就可以按照路径直接访问 3).页面放在templates下,直接访问 springboot ,访问项目的时 ...

  8. DPDI(Dispatch PDI)kettle调度管理平台发布新版本了

    Dispatch PDI最新版本发布! 我们很高兴地宣布,Dispatch PDI的全新轻量级版本现已在官网上线!这款专为高效ETL任务调度和监控设计的平台,将为您的数据处理带来前所未有的便捷. 立即 ...

  9. 大模型向量数据库去重的N种实现方案!

    简单来说,"向量"Vector 是大模型(LLM)在搜索时使用的一种"技术手段",通过向量比对,大模型能找出问题的相关答案,并且进行智能回答. 向量简介 Vec ...

  10. 3d xna fbx winfrom 读取

    本文通过参考网上资源做的一个例子. 本程序的功能就是通过xna 将3d 图像显示到winfrom 对他进行旋转操作. 首先我们先准备好两个文件夹 model  文件夹放fbx文件,textures 放 ...