Luogu P8479 「GLR-R3」谷雨
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;
}