P6773 [NOI2020] 命运

题面

给定一棵树,你需要给树上每条边确定染色。

同时给定 \(m\) 个限制,每条限制形如 \(u\)\(v\) 路径上的边中存在一个黑色边,保证 \(u\)\(v\) 的祖先。

问所有染色方案数模 \(998244353\)

数据范围: \(n,m\le 5\times 10^5\)

题解

考虑一下链的情况,其实就是 CF1327F AND Segments

在那个题中,我们维护了 \(f_j\) 表示最后一个 \(1\)\(j\) 这个位置时的方案数,然后随着当前 \(i\) 的增加,不断消除掉不合法的 \(f_j\) ,同时维护总和来做到 \(O(1)\) 转移。

但这题中是树的结构,我们无法做到消除不合法的状态,所以需要换一种做法。

考虑反过来,设 \(f_{i,j}\) 表示考虑以 \(i\) 为根的子树,其中未满足的限制中 \(u\) 深度最大的为 \(j\)

什么意思呢?我们把每个限制存在 \(v\) 上,对于相同的 \(v\) ,我们只保留深度最大的(深度大的满足了,那么深度小的一定满足),记为 \(b_v\)

初始 \(f_{x,b_x}=1\),其余为 \(0\),考虑合并子树:

  • \((x,y)\) 染成黑色,那么 \(y\) 子树内的限制一定都能满足,除了 \(f_{y,j}\) ,其中 \(j>de_x\) ,那么这些就在 \(y\) 这就不能满足了。

    \(f_{x,i}\leftarrow f_{x,i}\sum_{j\le de_x} f_{y,j}\)

  • \((x,y)\) 染成白色,那么相当于 \(y\) 子树内的是咋样还是咋样,即 \(f_{x,i}\leftarrow f_{x,i}\sum _{j\le i}f_{y,j}+f_{y,i}\sum_{j<i} f_{x,j}\)

现在就有 \(n^2\) 的DP了,即

\[f_{x,i}\leftarrow f_{x,i}(\sum_{j\le de_x} f_{y,j}+\sum _{j\le i}f_{y,j})+f_{y,i}\sum_{j<i} f_{x,j} \]

重点是后面的优化过程,我们给每个节点 \(x\) 开一个线段树,用来维护 \(f_{x,i}\)

虽然这个DP式子看起来很复杂,但是我们发现只有 \(\sum_{j\le de_x} f_{y,j}\) 不和 \(i\) 相关,而这个就是一个前缀和,可以在线段树上直接查询。

而后面的每一个都是一个前缀和的形式,我们可以在线段树合并的时候记录一下两棵树的前缀和,同时更新。

具体可以参考一下代码:

ll su1,su2=query(root[y],de[x],0,N);//查询在y树上的前缀和。
int hb(int p1,int p2,int l,int r){//直接合并。
	if(!p1&&!p2)return 0;
	if(!p1){
		su2=(su2+tr[p2].su)%mod;
		chuli(p2,su1);return p2;//chuli 是处理这个区间乘上某个数。
	}
	if(!p2){
		su1=(su1+tr[p1].su)%mod;
		chuli(p1,su2);return p1;
	}
	if(l==r){//注意顺序。
		su2=(su2+tr[p2].su)%mod;chuli(p2,su1);
		su1=(su1+tr[p1].su)%mod;chuli(p1,su2);
		tr[p1].su=(tr[p1].su+tr[p2].su)%mod;
		return p1;
	}
	down(p1),down(p2);
	int mid=(l+r)>>1;
	tr[p1].ls=hb(tr[p1].ls,tr[p2].ls,l,mid);
	tr[p1].rs=hb(tr[p1].rs,tr[p2].rs,mid+1,r);
	up(p1);
	return p1;
}

实现的时候需要注意一下更新前缀和、修改的顺序。

之后就没了,复杂度是 \(O(n\log n)\) 的。

启发

  • 这道题告诉我们这种 树上+关于前缀和的 \(n^2\) DP 是可以通过线段树合并做到 \(O(n\log n)\) 的。
posted @ 2022-03-30 14:32  qwq_123  阅读(58)  评论(0编辑  收藏  举报