P10789 [NOI2024] 登山 题解

先修改原题中的定义:\(l_i=d_i-r'_i\)\(r_i=d_i-l'_i\)\(h_i=d_i-h'_i-1\)\(h_{\text{path}(u,v)}=\min\limits_{k\in\text{path}(u,v)}h_k\)

\(f_u\) 表示从 \(u\)\(1\) 的方案数,考虑转移一定是先坠到子树某个节点,然后再冲到比 \(u\) 更高的一个点 \(k\),于是 \(f_u\) 需要从 \(f_k\) 转移,稍加推导可以得出转移式为

\[f_u=\sum_{v\in\text{sub}(u)}\sum_{k\in\text{path}(1,u)}[l_v\le d_k\le\min\{h_{\text{path}(u,v)},r_v\}]f_k \]

此时不难做到 \(O(n^2)\)。注意到虽然我们要转移的是 \(f_u\),但真正决定 \(f_u\) 的是 \(v\),所以不妨从 \(v\) 的角度来考虑哪些 \(u\) 会受到贡献。同时会发现能转移到 \(u\) 的 \(k\) 是一段区间,因此套路地差分为 \(s_{\min\{h_{\text{path}(u,v)},r_v\}}-s_{l_v-1}\)。现在枚举 \(v\),考虑会对哪些 \(u\) 造成贡献。

发现加入没有这个 \(h\) 的限制,我们随便处理:只需给树上一段祖先链整体加一个数,用树状数组维护即可。现在对于固定的 \(v\),真正在变化的是 \(h_{\text{path}(u,v)}\),并且这个值从 \(v\) 往上是单调不增的。所以分类讨论:

  • \(h_{\text{path}(u,v)}<l_v\),此时的这个 \(u\) 已经不能被 \(v\) 转移到了。所以倍增可以找出从 \(v\) 往上第一个这样的 \(u\),那么这个 \(u\) 以下的所有点都是可以被转移到的,给这些点加上 \(-s_{l_v-1}\) 的贡献;
  • \(r_v\le h_{\text{path}(u,v)}\),此时同理倍增找出第一个 \(h_{\text{path}(u,v)}<r_v\) 的点,给它以下的所有点加上 \(s_{r_v}\)
  • 发现还剩下中间 \(l_v\le h_{\text{path}(u,v)}<r_v\) 的点没有转移到。发现使得 \(h_{\text{path}(u,v)}\) 变化的点是后缀最小值的位置,考虑维护每个点的贡献系数,用差分维护,结合代码理解即可。

太妙了。

#include<bits/stdc++.h>
#define fw fwrite(obuf,p3-obuf,1,stdout)
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
#define putchar(x) (p3-obuf<1<<20?(*p3++=(x)):(fw,p3=obuf,*p3++=(x)))
#define il inline
using namespace std;

char buf[1<<20],obuf[1<<20],*p1=buf,*p2=buf,*p3=obuf,str[20<<2];
il int read(){
	int x=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
	return x;
}
il void write(int x,char sf='\n'){
	int top=0;
	do str[top++]=x%10,x/=10;while(x);
	while(top)putchar(str[--top]+48);
	putchar(sf);
}
constexpr int MAXN=1e5+5,MOD=998244353;
int T,n,p[MAXN],l[MAXN],r[MAXN],h[MAXN];
int head[MAXN],tot;
struct{
	int v,to;
}e[MAXN];
il void addedge(int u,int v){
	e[++tot]={v,head[u]};
	head[u]=tot;
}
il void add(int&x,int y){
	x=x+y>=MOD?x+y-MOD:x+y;
}
namespace B{
	#define lowbit(x) (x&-x)
	int c[MAXN];
	il void init(){
		memset(c,0,(n+1)<<2);
	}
	il void add(int x,int k){
		if(!x) return;
		for(;x<=n;x+=lowbit(x)) ::add(c[x],k);
	}
	il int sum(int x){
		int res=0;
		for(;x;x-=lowbit(x)) ::add(res,c[x]);
		return res;
	}
	il int sum(int l,int r){
		return (sum(r)-sum(l-1)+MOD)%MOD;
	}
}
int F,dep[MAXN],dfn[MAXN],rnk[MAXN],siz[MAXN],dfncnt;
int fa[17][MAXN],mn[17][MAXN],s[MAXN];
vector<tuple<int,int,int>>v[MAXN];
il void dfs(int u){
	dfn[u]=++dfncnt;
	rnk[dfn[u]]=u;
	siz[u]=1;
	for(int i=head[u],v;i;i=e[i].to) dfs(v=e[i].v),siz[u]+=siz[v];
}
il int gtk(int u,int k){
	for(int i=F;~i;i--){
		if(k>=1<<i) k-=1<<i,u=fa[i][u];
		if(!k) break;
	}
	return u;
}
il int gtmn(int u,int lim){
	for(int i=F;~i;i--)
		if(mn[i][u]>=lim)
			u=fa[i][u];
	return u;
}
il void dfs2(int u){
	s[u]=s[p[u]];
	add(s[u],B::sum(dfn[u],dfn[u]+siz[u]-1));
	for(auto p:v[u]){
		int x=get<0>(p),y=get<1>(p),z=1ll*get<2>(p)*s[u]%MOD;
		B::add(dfn[x],MOD-z);
		B::add(dfn[y],z);
	}
	for(int i=head[u];i;i=e[i].to) dfs2(e[i].v);
}
il void sve(){
	n=read();
	F=__lg(n);
	dep[1]=0;
	for(int i=2;i<=n;i++){
		p[i]=read(),l[i]=read(),r[i]=read(),h[i]=read();
		addedge(p[i],i);
		dep[i]=dep[p[i]]+1;
	}
	for(int i=1;i<=n;i++){
		fa[0][i]=p[i];
		mn[0][i]=h[i]=dep[i]-h[i]-1;
		l[i]=dep[i]-l[i],r[i]=dep[i]-r[i];
		swap(l[i],r[i]);
	}
	for(int j=1;j<=F;j++)
		for(int i=1;i<=n;i++){
			fa[j][i]=fa[j-1][fa[j-1][i]];
			mn[j][i]=min(mn[j-1][i],mn[j-1][fa[j-1][i]]);
		}
	dfs(1);
	for(int i=2,x;i<=n;i++){
		x=gtmn(i,l[i]);
		s[x]--;
		v[gtk(i,dep[i]-l[i]+1)].emplace_back(x,i,MOD-1);
		x=gtmn(i,r[i]);
		s[x]++;
		v[gtk(i,dep[i]-r[i])].emplace_back(x,i,1);
	}
	for(int i=n,u;i;i--){
		if(!s[u=rnk[i]]) continue;
		int f=gtmn(u,h[u]);
		v[gtk(u,dep[u]-h[u])].emplace_back(f,u,s[u]);
		s[f]+=s[u];
	}
	memset(s,0,(n+1)<<2);
	s[0]=1;
	dfs2(1);
	for(int i=2;i<=n;i++) write((s[i]-s[p[i]]+MOD)%MOD," \n"[i==n]);
	memset(s,0,(n+1)<<2);
}
il void cls(){
	memset(head,0,(n+1)<<2);
	tot=dfncnt=0;
	B::init();
	for(int i=0;i<=n;i++) v[i].clear();
}

int main(){
	freopen("mountain.in","r",stdin);
	freopen("mountain.out","w",stdout);
	read(),T=read();
	while(T--) sve(),cls();
	return fw,0;
}
posted @ 2026-01-11 21:26  D3509  阅读(8)  评论(0)    收藏  举报