W
e
l
c
o
m
e
: )

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;
}
posted @ 2022-09-15 21:18  127_127_127  阅读(110)  评论(0编辑  收藏  举报