LGP6773题解

阴间状态,出题人是怎么想到的。。。

为啥lg题解全部都是直接丢状态不说是怎么想的啊。要是以后遇到阴间状态题该怎么想.jpg

首先通过观察,我们可以形象地定义染色:边权为 \(1\) 的边相当于将此边割掉,边权为 \(0\) 的边相当于不割掉。(这个是为了方便思考)

我们要做的就是让祖先不能到达孙子。但是这样好像比较困难,因为实际上一个祖先可能有多个孙子。。。

所以我们考虑让孙子无法到达祖先。我们对孙子枚举一个不超过不超过指定祖先的祖先,表示割掉这个祖先到其父亲的边。

如果割掉这条边,那么这个祖先子树中的所有条件一定都会被满足。

我们先设计一个很傻逼的 DP:\(dp[u][S]\) 表示子树中还有 \(S\) 这些限制没有被满足。

然后我们可以判断自己的父亲是否是这些限制的祖先节点来进行转移。

你发现一件事情:如果我割掉了这条边,那么我子树中的所有限制都被满足了。并且我只需要判定我的父亲是否是这些节点的祖先。

也就是说,我只需要知道这些祖先中,最深的节点就足够判定了。管这么多干嘛?

所以,设 \(dp[u][k]\)\(u\) 子树中,未被满足的限制中,祖先节点最深的那个的深度为 \(k\)

我们可以根据这个写出转移:

\[dp[u][k]=(\sum_{i=0}^{d[u]}dp[u][k]\times dp[v][i])+((\sum_{i=0}^{k}dp[u][k]\times dp[v][i])+(\sum_{i=0}^{k-1}dp[u][i]\times dp[v][k])) \]

对于一个节点的初始值,假设所有限制的孙子节点为这个节点的集合为 \(S\),那么我们只需要找到 \(S\) 中祖先节点最深的深度 \(d\),让 \(dp[u][d]=1\) 就行了。

如果 \(S\) 为空,就让 \(dp[u][0]=1\)。因为到后面和其他信息合并的时候,是取一个 \(\max\),自身的深度信息肯定会被遗弃掉。

使用前缀和优化可以做到 \(O(nm)\)

注意到,每个节点的初始有值的位置只可能有一个,并且满足 \(dp[u][k]=\sum_{\max(x,y)=k}f(dp[u][x],dp[v][y])\) 的模型,可以使用线段树合并来优化。

具体细节见代码:

#include<cstdio>
typedef unsigned ui;
const ui M=5e5+5,mod=998244353;
ui n,m,cnt,tot,d[M],f[M],up[M],h[M],root[M];
struct Edge{
	ui v,nx;
}e[M<<1];
inline void Add(const ui&u,const ui&v){
	e[++cnt]=(Edge){v,h[u]};h[u]=cnt;
	e[++cnt]=(Edge){u,h[v]};h[v]=cnt;
}
struct Node{
	ui L,R,sum;
	ui tmul;
	inline void update(const ui&cmul){
		sum=1ull*cmul*sum%mod;
		tmul=1ull*tmul*cmul%mod;
	}
	inline void clear(){
		tmul=1;
	}
}t[M*30];
inline void pushdown(const ui&u){
	if(t[u].L)t[t[u].L].update(t[u].tmul);if(t[u].R)t[t[u].R].update(t[u].tmul);t[u].clear();
}
inline void update(const ui&u){
	t[u].sum=(t[t[u].L].sum+t[t[u].R].sum)%mod;
}
inline void Insert(ui&u,const ui&x,const ui&L=0,const ui&R=n){
	if(!u)t[u=++tot].clear();++t[u].sum;
	if(L<R){
		const ui mid=L+R>>1;
		if(x<=mid)Insert(t[u].L,x,L,mid);
		else Insert(t[u].R,x,mid+1,R);
	}
}
inline ui Qid(const ui&u,const ui&x,const ui&L=0,const ui&R=n){
	if(L==R)return t[u].sum;
	const ui mid=L+R>>1;pushdown(u);
	return x<=mid?Qid(t[u].L,x,L,mid):(t[t[u].L].sum+Qid(t[u].R,x,mid+1,R))%mod;
}
inline void Merge(ui&q,const ui&p,const ui&S1,const ui&S2,const ui&L=0,const ui&R=n){
	if(!q||!p){
		if(q)t[q].update(S1);
		if(p)t[p].update(S2);
		return void(q|=p);
	}
	if(L==R){
		t[q].sum=(1ull*t[q].sum*(S1+t[p].sum)+1ull*t[p].sum*S2)%mod;
		return;
	}
	const ui mid=L+R>>1;pushdown(q);pushdown(p);
	Merge(t[q].R,t[p].R,(S1+t[t[p].L].sum)%mod,(S2+t[t[q].L].sum)%mod,mid+1,R);
	Merge(t[q].L,t[p].L,S1,S2,L,mid);
	update(q);
}
inline void init(const ui&u){
	d[u]=d[f[u]]+1;
	for(ui v,E=h[u];E;E=e[E].nx)if((v=e[E].v)^f[u])f[v]=u,init(v);
}
inline void DFS(const ui&u){
	Insert(root[u],up[u]);
	for(ui v,E=h[u];E;E=e[E].nx)if((v=e[E].v)^f[u]){
		DFS(v);Merge(root[u],root[v],Qid(root[v],d[u]),0);
	}
}
signed main(){
	scanf("%u",&n);
	for(ui i=1;i<n;++i){
		ui u,v;scanf("%u%u",&u,&v);Add(u,v);
	}
	init(1);
	scanf("%u",&m);
	while(m--){
		ui u,v;scanf("%u%u",&u,&v);if(d[u]>up[v])up[v]=d[u];
	}
	DFS(1);
	printf("%u",Qid(root[1],0));
}
posted @ 2022-03-09 08:45  Prean  阅读(25)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};